diff --git a/.gitignore b/.gitignore index 5d15477..5bce97a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -installed.json \ No newline at end of file +installed.json +.vscode/ \ No newline at end of file diff --git a/Instructions.md b/Instructions.md new file mode 100644 index 0000000..17f5d0c --- /dev/null +++ b/Instructions.md @@ -0,0 +1,40 @@ +## Установка пакета в ArduinoIDE +1. Установите [Arduino IDE](https://www.arduino.cc/en/software). +2. Откройте меню `Файл -> Параметры`. +3. Вставьте данную ссылку в поле "Дополнительные ссылки для Менеджера плат": +`https://elron.tech/files/package_elbear_beta_index.json` +![Add_board](docs/Add_board.PNG) +4. Откройте меню `Инструменты -> Плата -> Менеджер плат...`. +5. В поиске найдите плату `Elbear Ace-Uno`, выберите нужную версию и нажмите кнопку `Установить`. +![Install_board](docs/Install_board.PNG) +6. Процесс установки может занять некоторое время. Результаты установки отобразятся в поле `Вывод`, а так же во всплывающих уведомлениях. +![Installation_process](docs/Installation_process.PNG) + +Для загрузки скетчей по USB в ArduinoIDE необходимо, чтобы на плате Elbear Ace-Uno был специальный начальный загрузчик ([elbear_fw_bootloader](https://gitflic.ru/project/elron-tech/elbear_fw_bootloader)). Если он уже есть на плате, можно сразу переходить к работе. Если загрузчика еще нет или необходимо обновить его на плате, ниже описан процесс загрузки. Актуальная версия начального загрузчика входит в состав пакета поддержки, отдельно скачивать его не нужно. + +Платы ревизии 1.1.0 готовы к использованию в ArduinoIDE из коробки, так как поставляются с предварительно записанным начальным загрузчиком. + +## Запись начального загрузчика через ArduinoIDE +1. Подключите плату Elbear Ace-Uno к ПК через программатор ELJTAG. +2. В ArduinoIDE выберите программатор: `Инструменты -> Программатор -> mik32 uploader`. +3. Для записи начального загрузчика выберите `Инструменты -> Записать Загрузчик`. +![Bootloader](docs/Bootloader.png) +4. При возникновении проблем с загрузкой ознакомьтесь с разделом `Настройка программатора` в [инструкции](https://elron.tech/wp-content/uploads/2024/05/instrukcija-po-pervomu-zapusku.pdf) по первому запуску платы ELBEAR ACE-UNO. +Теперь можно загружать скетчи в плату по USB. + +## Начало работы +1. Подключите плату к ПК по USB. +2. Откройте ArduinoIDE и загрузите необходимый скетч. Для начала работы можно воспользоваться готовыми примерами, например - `Файл -> Примеры -> 01.Basics -> Blink`. +![Blink_example](docs/Blink_example.png) +3. Выберите активную плату - `Инструменты -> Плата`. +![Set_board](docs/Set_board.png) +4. Выберите используемый COM порт - `Инструменты -> Порт`. +![Set_port](docs/Set_port.png) +Выбранные плата и порт в ArduinoIDE должны отображаться следующим образом: +![Selected_board_port](docs/Selected_board_port.png) +5. Проверьте скетч, нажав соответствующую кнопку. +![Build_project](docs/Build_project.png) +6. Загрузите полученную прошивку на плату. +![Flash_project](docs/Flash_project.png) +7. При необходимости можно открыть терминал и получать сообщения от платы по интерфейсу Serial. Для этого добавьте в скетч работу с интерфейсом и после загрузки прошивки выберите `Инструменты -> Монитор порта`. +![Monitor](docs/Monitor.png) \ No newline at end of file diff --git a/README.md b/README.md index 8945443..15cfcc8 100644 --- a/README.md +++ b/README.md @@ -3,49 +3,93 @@ ## Установка пакета в ArduinoIDE -1. Установите [Arduino IDE](https://www.arduino.cc/en/software). -2. Откройте меню `Файл -> Параметры`. -3. Вставьте данную ссылку в поле "Дополнительные ссылки для Менеджера плат": -`https://elron.tech/files/package_elbear_beta_index.json` -![Add_board](docs/Add_board.PNG) -4. Откройте меню `Инструменты -> Плата -> Менеджер плат...`. -5. В поиске найдите плату `Elbear Ace-Uno`, выберите нужную версию и нажмите кнопку `Установить`. -![Install_board](docs/Install_board.PNG) -6. Процесс установки может занять некоторое время. Результаты установки отобразятся в поле `Вывод`, а так же во всплывающих уведомлениях. -![Installation_process](docs/Installation_process.PNG) +Для установки пакета в параметрах ArduinoIDE необходимо добавить ссылку `https://elron.tech/files/package_elbear_beta_index.json` в поле "Дополнительные ссылки для Менеджера плат". +Подробные шаги по установке и начальной настройке описаны в [инструкции](./Instructions.md). -Для загрузки скетчей по USB в ArduinoIDE необходимо, чтобы на плату Elbear Ace-Uno была загружена специальная программа-загрузчик ([elbear_fw_bootloader](https://gitflic.ru/project/elron-tech/elbear_fw_bootloader)). Если она уже есть на плате, можно сразу переходить к работе. Если загрузчика еще нет или необходимо обновить его на плате, ниже описан процесс загрузки. Актуальная версия программы-загрузчика входит в состав пакета поддержки, отдельно скачивать её не нужно. +## Функциональное назначение выводов +![Pinout](docs/pinout.PNG) -Платы ревизии 1.1.0 готовы к использованию в ArduinoIDE из коробки, так как поставляются с предварительно загруженной программой-загрузчиком. +## Особенности использования платы Elbear Ace-Uno в ArduinoIDE +### Цифровые выводы +На плате Elbear Ace-Uno доступны встроенные светодиод и кнопка. Для их использования необходимо воспользоваться макросами `LED_BUILTIN` и `BTN_BUILTIN`, передавая их в качестве аргументов функции вместо номера цифрового вывода. Макросу `LED_BUILTIN` соответствует номер вывода D22, а макросу `BTN_BUILTIN` - D23. +В отличие от стандартного функционала Arduino, на плате Elbear Ace-Uno невозможно управлять притяжками цифрового вывода, настроенного на вход, с помощью функции `void digitalWrite(uint32_t PinNumber, uint32_t Val)`. Для включения притяжки к питанию необходимо воспользоваться функцией `void pinMode(PinNumber, INPUT_PULLUP)`. +Для инвертирования состояния цифровых выводов доступна функция `void digitalToggle(uint32_t PinNumber)`. -## Загрузка программы-загрузчика через ArduinoIDE -1. Подключите плату Elbear Ace-Uno к ПК через программатор ELJTAG. -2. В ArduinoIDE выберите программатор: `Инструменты -> Программатор -> mik32 uploader`. -3. Для загрузки программы-загрузчика выберите `Инструменты -> Записать Загрузчик`. -![Bootloader](docs/Bootloader.png) -4. При возникновении проблем с загрузкой ознакомьтесь с разделом `Настройка программатора` в [инструкции](https://elron.tech/wp-content/uploads/2024/05/instrukcija-po-pervomu-zapusku.pdf) по первому запуску платы ELBEAR ACE-UNO. -Теперь можно загружать скетчи в плату по USB. +### Аналоговые выводы +#### АЦП +Встроенный в MIK32 АЦП обладает разрешением 12 бит, однако по умолчанию в Arduino IDE применяется разрешение 10 бит. С помощью функции `void analogReadResolution(uint8_t resolution)` можно изменять разрешение в диапазоне от 1 до 32 бит. +Функция `uint32_t analogRead(uint32_t PinNumber)` возвращает результаты измерения после усреднения по 10 значениям. +#### ШИМ +На плате Elbear Ace-Uno доступны следующие выводы для формирования ШИМ-сигнала: D3, D5, D6, D9, D10, D11. Генерация сигнала осуществляется с помощью 32-битного таймера. Выводы D3, D5, D6, D9 подключены к таймеру 1, выводы D10, D11 подключены к таймеру 2. Выводы, подключенные к одному и тому же таймеру, выдают ШИМ-сигнал одинаковой частоты. +Цифровой вывод D10 не может быть использован для генерации ШИМ, если одновременно активен интерфейс SPI. Это ограничение связано с особенностями работы микроконтроллера. Ограничение не распространяется на использование D10 в качестве цифрового вывода при активном SPI. +По умолчанию частота сформированного ШИМ-сигнала составляет 1 кГц. Функция `void analogWriteFrequency(uint32_t freq)` позволяет изменить частоту сигнала в диапазоне от 1 Гц до 1 МГц. +По умолчанию разрешение, используемое в функции `void analogWrite(uint32_t PinNumber, uint32_t writeVal)`, составляет 8 бит. Функция `void analogWriteResolution(uint8_t resolution)` позволяет измененить разрешение в диапазоне от 1 до 32 бит. +Остановить генерацию ШИМ-сигнала можно, вызвав функцию `void analogWriteStop(uint32_t PinNumber)` или функции `void digitalWrite(uint32_t PinNumber, uint32_t Val)`/`int digitalRead(uint32_t PinNumber)`. -## Начало работы -1. Подключите плату к ПК по USB. -2. Откройте ArduinoIDE и загрузите необходимый скетч. Для начала работы можно воспользоваться готовыми примерами, например - `Файл -> Примеры -> 01.Basics -> Blink`. -![Blink_example](docs/Blink_example.png) -3. Выберите активную плату - `Инструменты -> Плата`. -![Set_board](docs/Set_board.png) -4. Выберите используемый COM порт - `Инструменты -> Порт`. -![Set_port](docs/Set_port.png) -Выбранные плата и порт в ArduinoIDE должны отображаться следующим образом: -![Selected_board_port](docs/Selected_board_port.png) -5. Проверьте скетч, нажав соответствующую кнопку. -![Build_project](docs/Build_project.png) -6. Загрузите полученную прошивку на плату. -![Flash_project](docs/Flash_project.png) -7. При необходимости можно открыть терминал и получать сообщения от платы по интерфейсу Serial. Для этого выберите `Инструменты -> Монитор порта`. -![Monitor](docs/Monitor.png) +### Прерывания +На плате Elbear Ace-Uno доступно 7 прерываний, настраиваемых функцией `void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode)`: + +|Цифровой вывод|Номер прерывания| +|---------|---------| +|D2|0| +|D3|1| +|D4|2| +|D5|3| +|D8|4| +|D9|5| +|`BTN_BUILTIN`|6| +Для получения номера прерывания по номеру вывода существует функция `int8_t digitalPinToInterrupt(uint32_t digPinNumber)`. + +В микроконтроллере MIK32 предусмотрен всего один вектор прерывания. Когда срабатывает прерывание от любого источника, общая функция-обработчик последовательно проверяет все возможные источники и, при необходимости, вызывает соответствующие обработчики конкретных модулей. Поэтому важно, чтобы функции, вызываемые при прерываниях, были небольшими и обеспечивали максимально быстрое завершение обработки. Это позволит избежать задержек и снизит риск пропуска последующих прерываний. +Общая функция-обработчик прерываний располагается в RAM памяти. Это позволяет устранить задержки, связанные с кэшированием при работе из FLASH памяти. Обработчики прерываний, назначаемые на цифровые выводы с помощью функции `void attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode)`, и обработчик прерывания для функции `tone()` так же располагаются в памяти RAM. + +### Serial +Для работы доступно два последовательных интерфейса. Нулевой интерфейс доступен на выводах D0, D1, для работы с ним используется экземпляр класса под названием `Serial`. Нулевой интерфейс используется для вывода информации в Монитор порта в Arduino IDE. +Первый интерфейс доступен на выводах D7, D8, используемый экземпляр класса - `Serial1`. +Доступны следующие настройки режима работы каждого интерфейса: длина данных - 7 или 8 бит; бит четности - нет, четный, нечетный; стоп бит - 1 или 2 бита. + +### Предупреждения об ошибках +Если в скетче используется интерфейс `Serial`, при возникновении ошибок при использовании какой-либо функции из пакета в порт может передаваться сообщение об этой ошибке с пояснением. Например, если в функцию будет передан некорректный номер цифрового вывода, предупреждение об этом появится в подключенном com порту. +По умолчанию вывод предупреждений включен. Если интерфейс `Serial` используется для коммуникации с другим устройством, вывод предупреждений можно отключить. Для этого в самом начале функции `void setup()` необходимо вызвать макрос `DISABLE_ERROR_MESSAGES();`. Вывод предупреждений можно включить обратно, вызвав макрос `ENABLE_ERROR_MESSAGES();` в любом месте программы. + +### Библиотеки, входящие в состав пакета +Входящие в состав пакета библиотеки используют периферию микроконтроллера MIK32 Амур и/или адаптированы для работы с ним. + +|Библиотека|Описание|Заметки| +|---------|---------|------| +|[SPI](https://docs.arduino.cc/language-reference/en/functions/communication/SPI/)|Библиотека для работы с интерфейсом SPI|Для работы используется встроенный SPI1. Доступные делители частоты - `SPI_CLOCK_DIV4`, `SPI_CLOCK_DIV8`, `SPI_CLOCK_DIV16`, `SPI_CLOCK_DIV32`, `SPI_CLOCK_DIV64`, `SPI_CLOCK_DIV128`, `SPI_CLOCK_DIV256`, обеспечивают частоту работы от 125 кГц до 8 МГц. Скорость работы по умолчанию - 4 МГц. Для задания режима и скорости работы рекомендуется использовать `SPISettings(uint32_t speedMaximum, uint8_t dataOrder, uint8_t dataMode)`, а не соответствующие отдельные функции| +|[Wire](https://docs.arduino.cc/language-reference/en/functions/communication/Wire/)|Библиотека для работы с интерфейсом I2C|Для работы используется встроенный I2C1. Доступные частоты работы интерфейса: 100 кГц (`WIRE_FREQ_100K`), 400 кГц (`WIRE_FREQ_400K`), 1000 кГц (`WIRE_FREQ_1000K`). Скорость работы по умолчанию - 100 кГц. В режиме работы в качестве ведомого устройства функции, заданные через `void onReceive( void (*)(int)` и `void onRequest( void (*)(void) )`, выполняются в прерывании| +|[SoftwareSerial](https://docs.arduino.cc/learn/built-in-libraries/software-serial/)|Библиотека, реализующая программный последовательный интерфейс.|Доступные скорости работы - от 300 до 57600 бод. Для отправки данных (TX) можно использовать любой цифровой вывод. Для приема данных (RX) можно использовать только выводы, поддерживающие прерывания. Обработчик прерывания и связанные с ним функции располагаются в памяти RAM| +|[EEPROM](https://docs.arduino.cc/learn/built-in-libraries/eeprom/)|Библиотека для работы с памятью EEPROM|Для использования доступно 1024 байта встроенной EEPROM памяти. Для корректной работы библиотеки обязательно вызывать функцию `void EEPROM.begin()` перед началом работы с памятью| +|[Servo](https://docs.arduino.cc/libraries/servo/)|Библиотека для работы с сервоприводом|Библиотека использует 16-битный таймер 2 и прерывания от него. Любой цифровой вывод подходит для управления сервоприводом. Одновременно можно использовать до 12 сервоприводов| +|[NeoPixel](https://docs.arduino.cc/libraries/adafruit-neopixel/)|Библиотека для работы с адресными светодиодами|Функция, выводящая состояние пикселей на цифровой вывод платы, перенесена в память RAM для корректной работы на MIK32 Амур| +|[MFRC522](https://docs.arduino.cc/libraries/mfrc522/)|Библиотека для работы с RFID картами|Исправлен баг, вызывающий ошибку компиляции в новых компиляторах gcc| + +## Протестированные библиотеки +|Библиотека|Описание| +|---------|---------| +|[RFID_MFRC522v2](https://docs.arduino.cc/libraries/rfid_mfrc522v2/)|Новая версия библиотеки MFRC522 для работы с RFID картами| +|[SD](https://www.arduino.cc/en/Reference/SD)|Библиотека, позволяющая считывать и записывать информацию на SD карты| +|[TimeLib](https://docs.arduino.cc/libraries/time/)|Библиотека для удобной работы с переменными времени| +|[Ds1302](https://reference.arduino.cc/reference/en/libraries/ds1302/)|Библиотека для работы с микросхемой часов реального времени DS1302| +|[DS1307RTC](https://docs.arduino.cc/libraries/ds1307rtc/)|Библиотека для работы с микросхемой часов реального времени DS1307| +|[microDS3231](https://docs.arduino.cc/libraries/microds3231/)|Легкая библиотека для работы с микросхемой часов реального времени DS3231| +|[Rtc](https://github.com/Makuna/Rtc/tree/master)|Библиотека для работы с разными микросхемами часов реального времени| +|[AHT10](https://github.com/enjoyneering/AHT10/tree/master)|Библиотека для работы с датчиками температуры и влажности AHT10, AHT15, AHT20| +|[DHT](https://docs.arduino.cc/libraries/dht-sensor-library/)|Библиотека для работы с датчиками температуры и влажности типа DHT| +|[Adafruit_BMP280](https://docs.arduino.cc/libraries/adafruit-bmp280-library/)|Библиотека для работы с датчиками давления и высоты BMP280| +|[MPU6050](https://reference.arduino.cc/reference/en/libraries/mpu6050/)|Библиотека для работы с акселерометром/гироскопом MPU6050| +|[Kalman](https://docs.arduino.cc/libraries/kalman-filter-library/)|Библиотека, реализующая фильтр Калмана| +|[LiquidCrystal_I2C](https://docs.arduino.cc/libraries/liquidcrystal-i2c/)|Библиотека для управления LCD дисплеями по интерфейсу I2C| +|[JoystickShield](https://github.com/sudar/JoystickShield/tree/master)|Библиотека для работы с шилдом JoystickShield| +|[RF24](https://docs.arduino.cc/libraries/rf24/)|Драйвер радиоустройств, библиотека для работы с микросхемами nRF24L01(+)| +|[Bonezegei_ULN2003_Stepper](https://docs.arduino.cc/libraries/bonezegei_uln2003_stepper/)|Библиотека драйвера шагового двигателя, управляемого микросхемой ULN2003| +|[Ethernet](https://docs.arduino.cc/libraries/ethernet/)|Библиотека, позволяющая использовать Ethernet шилд для подключения к Интернету| -При возникновении вопросов или выявлении проблем можно оставить заявку [здесь](https://gitflic.ru/project/elron-tech/elbear_arduino_bsp/issue). # Полезные ссылки * [Материалы по платам ELBEAR ACE-UNO](https://elron.tech/support/#elbear) * [Телеграмм-канал компании (обновления по проекту ELBEAR и другим)](https://t.me/elrontech) + +При возникновении вопросов или выявлении проблем можно оставить заявку [здесь](https://gitflic.ru/project/elron-tech/elbear_arduino_bsp/issue). diff --git a/bootloaders/ace-uno/bootloader.hex b/bootloaders/ace-uno/bootloader.hex index 4274be7..3e826ca 100644 --- a/bootloaders/ace-uno/bootloader.hex +++ b/bootloaders/ace-uno/bootloader.hex @@ -1,15 +1,15 @@ :020000040100F9 :10000000FD62938202400100FD12E39E02FE374131 :10001000000213010100B701000293810100B7152E -:100020000001938505F537160001130606F8B7069B +:100020000001938505FF3716000113060602B70687 :1000300000029386060039A083A2050023A0560083 -:1000400091059106E3EAC5FEB7150001938505F811 -:1000500037160001130606F8B70600029386062637 +:1000400091059106E3EAC5FEB71500019385050207 +:100050003716000113060602B7060002938606262D :1000600039A083A2050023A0560091059106E3EA7A :10007000C5FEB70500029385050337060002130687 :10008000062621A023A005009105E3EDC5FEB700DB -:100090000001E780C00AB7000001E780C00AB7008E -:1000A0000001E780207B73005010F5BF82800000C4 +:100090000001E780C00AB7000001E780C00AB7107E +:1000A0000001E780808473005010F5BF828000005B :1000B0000000000000000000000000000000000040 :1000C0006F004000197106C20AC40EC612C816CAD3 :1000D0001ACC1ECE22D026D22AD42ED632D836DA48 @@ -39,213 +39,223 @@ :1002500008009387074023A0070023A2070023A4D8 :10026000070023A607007D57D8CF23A40702B7179E :100270000500938707C09843F1769386F63F758F04 -:1002800098C398471377F7BF98C7B7070500D84FAB -:1002900023AE07008280B71708009387074088D7EE -:1002A000D84F137707046DDF8280370700020323DE -:1002B000C703B706000237150800B7450F0023A093 -:1002C000060481470146130505409385152403283C -:1002D000C501B308F30013780802630C080205C2D5 -:1002E000232E170337470F0023A0F6041307072414 -:1002F0006397E700B707000205472383E700B717B6 -:10030000080093870740C853420541818280054613 -:100310007DBF8507E39DB7FE7DD2B7470F0093876A -:10032000072423A0F604232E1703E9B7411106C6BC -:1003300022C426C2AA84EF003019E1689388086AB3 -:1003400001488147014781460146B70520C72685F8 -:1003500037949800EF00A07C130414687D1419E40E -:100360000D45B24022449244410182808545268554 -:10037000EF00F01605897DF10145E5B7411122C472 -:1003800037040002930704008C43B70700804AC07B -:10039000BE95B707000223AAB70206C626C29397E6 -:1003A0004501130404003709000289E71305890099 -:1003B000EF0010200C40B70400029386440413069B -:1003C000001013058900EF00F0191C4037070002E8 -:1003D00083562703938707101CC013060010B687A7 -:1003E00063F4C60093070010138444041305F4005B -:1003F000938707F01306100F814513040410231987 -:10040000F702893BA285138544043D46EF00901E08 -:1004100023200400232204002324040023160400C4 -:1004200023070400B24022449244024941018280E1 -:100430005D71130680028145280886C6213BBD47B1 -:10044000230CF1008947230EF1003ED2E177938718 -:1004500007082C080A85231AF1028523B64061613A -:1004600082805D71A2C4370400021305840086C631 -:10047000A6C2092D13058400EF00207489451305D9 -:100480008400EF00D0059377250085E3AA84854595 -:1004900013058400EF00B00413E62400AA85137648 -:1004A000F60F13058400EF00F006E1689388086AF0 -:1004B00001488147014781460146B70520381305A9 -:1004C0008400B12DE16838009388086A130800029F -:1004D000854681470146B70599EB1305840023043F -:1004E0000100352DB715000151469385C5F368000D -:1004F000EF005010930484009C406C0051463ED0A5 -:1005000085473ED26810C1673ED4EF00B00EB706F3 -:1005100007009C423707F1FF7D17F98F08109CC236 -:100520006523B6402644964461618280411106C627 -:10053000393B053FB707008073905730B700008004 -:100540008290B240410182804111B707000222C46B -:100550001387070006C68346670005471384070014 -:10056000639EE600B717080093870740D84F218B9A -:1005700009C7D84F13678700D8CF4D3F2303040026 -:10058000B240224441018280411106C622C426C2E3 -:100590004AC08347350005476383E70811472A842B -:1005A0006389E7008DCFB240224492440249410161 -:1005B0008280B707000283D7270391C3C1338347E3 -:1005C000440003475400E2074207BA973707000286 -:1005D000232CF702370700022320F700E9B7370979 -:1005E000000283542903034605009305450037059F -:1005F0000002130545042695EF00C07F83470400E1 -:10060000BE94C204C180231999029307F00FE3FC42 -:1006100097F82244B24092440249410185B33D45D6 -:100620009D39B707000283D7270391C3813B22443A -:10063000B240924402494101D5BD370700029307F9 -:100640000700B705000203DE470083A7050437163D -:10065000080037450F0037480F00B7080002014374 -:1006600081461307070013060640130515241308D7 -:1006700008249388481563EEC6016304030023A091 -:10068000F50483476700A9E7370500021305451500 -:10069000E5BD81470323C60113730302631F0300F3 -:1006A0008507E399A7FEB7470F009387072423A088 -:1006B000F50485472303F7008280E38607FF832E36 -:1006C00046023383D80085062300D301054365B76E -:1006D0008280011122CC4EC652C437040002B749B1 -:1006E0000F00371ADCBA26CA4AC856C25AC006CE0C -:1006F00013040400370900029389F923930A0003C5 -:10070000B7040002391A370B000245368327C903A4 -:10071000A303A40063F3F900113D8347640099C368 -:100720002535E5B7834674006388560593070006B0 -:100730006381F60603C704038D47998F8E07B3576D -:10074000FA0093F7F70F6393F6069307170093F7F2 -:10075000F70F2388F4021147639DE7003D45253ECE -:1007600013058B00E13629C11305000F2D362388B0 -:100770000402232E090251BF3D45232E090223887E -:100780000402113E1D3605052312A4003D45213605 -:10079000ADBF3D45232E090223880402ED3C713D87 -:1007A00083476400B5FFDDB73D45C9B72388040220 -:1007B000A9BF011106CE22CCA539370400021305CA -:1007C0008400C92413058400CD2EE1689388086A4B -:1007D00001488147780085460146B705D9EB1305E6 -:1007E000840023060100252EE1689388086A0148E9 -:1007F0008147014781460146B70538FF130584004C -:10080000392EED3AF9350547AA876305E502094710 -:100810006300E506054591EBB7060600DC4A7D77E7 -:100820001307F73FF98FDCCA014582807D1719EB6A -:100830000D4582809306004037A707001307071273 -:10084000B7050500905D7D8E75D2370606005C4ABF -:100850007D771307F73FF98FD58F41115CCA02C628 -:1008600013073006B2476359F70001454101828002 -:10087000856693860680C9B7B24785073EC6DDB751 -:1008800091476307F50263EAA7008547630AF50409 -:1008900089476309F50405458280A147E31DF5FEFC -:1008A0000947094501A8FD1781EFC8D20D4582808F -:1008B00005470D45B7A7070093870712B706050040 -:1008C000905E798E6DD28A05C98D4111CCD202C657 -:1008D00013073006B247635AF70001454101828091 -:1008E0001147C9BF21470145F1B7B24785073EC649 -:1008F000D5B70547AA876305E50209476302E50600 -:10090000054591EBB70606009C4A7D771307F73F34 -:10091000F98F9CCA014582807D1719EB0D458280B5 -:100920009306004037A7070013070712B705050015 -:10093000905D7D8E75D2370706001C4B7D761306C1 -:10094000F63FF18FD58F1CCB85471CCF411102C6D6 -:1009500013073006B2476359F70001454101828011 -:100960008566938606807DBFB24785073EC6DDB7A4 -:1009700011C98547630DF50205458280FD1791EB8E -:100980000D4582800946B7A7070093870712B7066F -:100990000500985E718F7DD34111C8D602C613073A -:1009A0003006B2476357F700014541018280054692 -:1009B000D9BFB24785073EC6EDB7011126CAB704B5 -:1009C0000600DC4806CE22CC4AC84EC652C456C2E7 -:1009D000F19BDCC89C482A89C845F19B9CC883C709 -:1009E000C5012E848A07DCC883C7D5018A079CC845 -:1009F000193D0C44AA8A0345440059351848B707E5 -:100A000005002A8A98C358480850D8C3184C98C77C -:100A1000CD35AA894850A93F834704002A8793F619 -:100A2000170089E6D44893E62600D4C893F6270039 -:100A300099E637060600544A93E6160054CA93F620 -:100A4000470099E637060600144A93E6260014CAC2 -:100A5000A18B99E7B70606009C4A93E717009CCA4A -:100A6000F240624423205901232249012324390101 -:100A70002326E900D244B249224A924A4A85424991 -:100A800005618280011106CE22CC02C402C6214734 -:100A9000B707050037550800D8C705448D478A8534 -:100AA000130505803EC022C2292A37550800930746 -:100AB000C0038A851305058022C222C43EC01122CC -:100AC000F240624405618280411122C406C62A8434 -:100AD000553F18405C4F93E707015CCF1C441CCB8B -:100AE0005C4085CB1C43B7061000D58F1CC3144C4B -:100AF0005C48B240D606CE07D58F83460401C206B5 -:100B0000D58F8346C4012244E206D58F1CCF410114 -:100B100082801C43B706F0FFFD16F58FC1BF03238B -:100B200005002A8E0325C30113650502232EA300A9 -:100B30002324C3001396260149824D8E23260301E8 -:100B40002322C300139605016354060299C205458A -:100B5000B1CB01476346D700639C08020D458280F4 -:100B60003386E700034606000507230AC300DDB706 -:100B700099C2054505CB8147E3D0D7FE03260E0079 -:100B8000034546013306F70085072300A600EDB7AD -:100B900083270E00FD18DC4F93F70702D5DF1165A0 -:100BA0006D8D11E18280B707070083C7470113F5F8 -:100BB00085001D8D3335A00082801C414147D8CF70 -:100BC0008280B7470800938707402A886304F508A6 -:100BD000B7570800938707806304F50A3747080072 -:100BE000630DE50A05458280331E1F013376DE0161 -:100BF00029C683A3450088431393180033966F00DA -:100C00001346F6FF13F43300718D33146400418DE5 -:100C100088C3638B5302638C0302084303AEC50091 -:100C2000718D331E6E003365C50108C38842698E1D -:100C3000884533156500498E90C2850833D51E015D -:100C400045F53244410182802326C801F9B72324A7 -:100C5000C801E1B7B716050037170500B71705003B -:100C6000938646C1130707C19387C7C083AE0500AB -:100C70008148054F8D4F914233D51E0105ED82808D -:100C8000B716050037170500B7170500938606C28B -:100C90001307C7C1938787C1D1BFB71605003717A0 -:100CA0000500B7170500938686C0130747C09387D2 -:100CB00007C06DBF331E1F013376DE0119E28508C0 -:100CC00065BF411122C635B7E1689388086A0148BB -:100CD0008147014781460146B705200689B50111C4 -:100CE00006CEA307010089476393F502B7052035B7 -:100CF000E1681307F1009388086A01488147854637 -:100D00000146313DF2400345F10005618280B7059F -:100D10002005F9BF011106CE22CC26CA2306B10058 -:100D2000AA84A306C1004D37E1689388086A014888 -:100D30007C00014789460146B78520012685096464 -:100D4000F93B130414717D1419E40D45F24062441B -:100D5000D24405618280854526855137058965F530 -:100D60000145EDB7011106CE22CC26CA2E844AC811 -:100D7000AA84328936C6893FB247E16822869388C1 -:100D8000086A01480147CA86B785800226856164E2 -:100D900079331304146A7D1411C48545268581377F -:100DA000058975F9F2406244D24442490561828066 -:100DB000011106CE22CC26CA2EC6AA84313732466D -:100DC000E1689388086A0148814701478146B70571 -:100DD000802026856164A1331304146A7D1411C434 -:100DE00085452685ED3D058975F9F2406244D2447A -:100DF00005618280B3C7A5008D8BB308C500B1E73C -:100E00008D4763F4C704937735002A87B9EB13F64F -:100E1000C8FFB306E6409307000263C8D706AE8654 -:100E2000BA876371C70203A806009107910623AE33 -:100E300007FFE3EAC7FE9307F6FF998FF19B91073F -:100E40003E97BE956366170182802A87637E1503ED -:100E500083C7050005078505A30FF7FEE39AE8FEA3 -:100E6000828083C60500050793773700A30FD7FE5E -:100E70008505D1DF83C60500050793773700A30FEB -:100E8000D7FE8505F9FF61B78280411122C61304A0 -:100E9000000283A3050083A2450083AF850003AF52 -:100EA000C50083AE050103AE450103A3850103A878 -:100EB000C501945113074702B307E640232E77FC80 -:100EC000232057FE2322F7FF2324E7FF2326D7FF03 -:100ED0002328C7FF232A67FE232C07FF232ED7FED4 -:100EE00093854502E347F4FAAE86BA876371C70279 -:100EF00003A806009107910623AE07FFE3EAC7FEA9 -:100F00009307F6FF998FF19B91073E97BE95636516 -:100F1000170132444101828083C70500050785051A -:100F2000A30FF7FEE387E8FE83C7050005078505E5 -:100F3000A30FF7FEE392E8FEE9BF000020000000E7 -:100F4000010000000300000006000000EB000000AC -:100F5000000000800000000000000700000000000A -:100F60000000000000000000000000000000000081 -:100F70000000000000000000000000000000000071 +:1002800098C398471377F7BF98C7B7070500094782 +:1002900098D38280B71708009387074088D7D84F34 +:1002A000137707046DDF82803707000203234704BA +:1002B000B706000237150800B7450F0023A406044F +:1002C0008147014613050540938515240328C50180 +:1002D000B308F30013780802630C080205C2232256 +:1002E000170537470F0023A4F60413070724639765 +:1002F000E700B707000205472383E700B7170800A8 +:1003000093870740C85342054181828005467DBFDF +:100310008507E39DB7FE7DD2B7470F00938707247B +:1003200023A4F60423221705E9B7411106C622C407 +:1003300026C2AA84EF00B022E1689388086A0148C7 +:100340008147014781460146B70520C72685379476 +:100350009800EF003006130414687D1419E40D456D +:10036000B240224492444101828085452685EF00B7 +:10037000702005897DF10145E5B7411122C437049C +:100380000002930704008C43B70700804AC0BE9563 +:10039000B707000223AEB70206C626C293974501EF +:1003A000130404003709000289E713058900EF00F0 +:1003B00090290C40B70400029386C4041306001071 +:1003C00013058900EF0070231C4037070002835695 +:1003D0008703938707101CC013060010B68763F4C9 +:1003E000C600930700101384C4041305F400938718 +:1003F00007F01306100F814513040410231CF702A5 +:10040000913BA2851385C4043D46EF0010282320AC +:1004100004002322040023240400231604002307DD +:100420000400B240224492440249410182805D713D +:10043000130680028145280886C6293BBD47230C48 +:10044000F1008947230EF1003ED2E1779387070838 +:100450002C080A85231AF102E52BB64061618280DF +:100460005D71A2C4370400021305840086C6A6C2CB +:10047000CAC0652513058400EF00807D85451305FE +:100480008400EF00300F8D47814463E3A700AA8406 +:10049000894513058400EF00F00D937725002A8627 +:1004A00089E7136625001376F60FA68513058400E9 +:1004B000EF00B00FE1689388086A0148814701475F +:1004C00081460146B705203813058400E525E1681B +:1004D00038009388086A1308000285468147014660 +:1004E000B70599EB1305840023040100E125B71536 +:1004F00000015146938505FD6800EF0010191309AE +:100500008400832709006C0051463ED085473ED2C7 +:100510006810C1673ED4EF005017B70607009C4231 +:100520003707F1FF7D17F98F08109CC20D2DB640DB +:1005300026449644064961618280411106C6013B0A +:100540000537B707008073905730B700008082905E +:10055000B2404101828041113707000222C406C621 +:100560009307070083C76700854613040700638E5F +:10057000D70009476388E70023030400B240224400 +:10058000410182801305000F3133B71708009387AC +:100590000740D84F218B09C7D84F13678700D8CFA2 +:1005A000693FD9BF411106C622C426C24AC083474B +:1005B000350005476383E70811472A846389E7000C +:1005C0008DCFB24022449244024941018280B70754 +:1005D000000283D7870391C34D3383474400034709 +:1005E0005400E2074207BA97370700022320F704B6 +:1005F000370700022320F700E9B7370900028354C8 +:1006000089030346050093054500370500021305DD +:10061000C5042695EF00700783470400BE94C2040A +:10062000C180231C99029307F00FE3FC97F8224442 +:10063000B24092440249410189B33D45A139B7070F +:10064000000283D7870391C30D3B2244B2409244FA +:1006500002494101DDB53707000241119306070049 +:1006600003DF460026C4B704000283A2840422C626 +:100670003715080037040002B7480F0037430F0052 +:10068000370E00024AC28147232A04021309FFFFE2 +:10069000814E814681458143014801461307070089 +:1006A000130505409388182413030324130ECE1555 +:1006B000636FE60363850E0009462303C70089C6FE +:1006C000B7060002A388F60299C1232A74026304C4 +:1006D000080023A4540483476700A9E73244A244D6 +:1006E0001249370500021305C515410165BD814258 +:1006F0000328C50113780802631B08028502E399E9 +:1007000012FF89C6B7060002A388F60299C1232A00 +:100710007402B7470F009387072423A4F404854786 +:100720002303F7003244A244124941018280E38A44 +:1007300062FC03284502937FF80F637D260113783E +:10074000F80FC29385453308CE002300F801050653 +:100750000548B9BFB307704093F7F70F6394FF00E4 +:100760008546D5B7854EEDBF011122CC4EC652C489 +:1007700037040002B7490F00371ADCBA26CA4AC844 +:1007800056C25AC006CE13040400370900029389EA +:10079000F923930A0003B7040002391A370B000249 +:1007A000213683274904A303A40063F3F9007133BE +:1007B0008347640099C34533E5B783467400638873 +:1007C0005605930700066381F60603C704038D47A9 +:1007D000998F8E07B357FA0093F7F70F6393F606D6 +:1007E0009307170093F7F70F2388F4021147639DCF +:1007F000E7003D45453413058B00053E29C113052F +:10080000000F493C238804022322090451BF3D45BF +:100810002322090423880402B53C793405052312F8 +:10082000A4003D45853CADBF3D45232209042388F6 +:1008300004028D340D3583476400B5FFDDB73D45B7 +:10084000C9B723880402A9BF011106CE22CCC53640 +:100850003704000213058400C92413058400CD2E3B +:10086000E1689388086A0148814778008546014617 +:10087000B705D9EB1305840023060100252EE16896 +:100880009388086A01488147014781460146B705B8 +:1008900038FF13058400392E9532F9350547AA87AC +:1008A0006305E50209476300E506054591EBB706D8 +:1008B0000600DC4A7D771307F73FF98FDCCA014554 +:1008C00082807D1719EB0D4582809306004037A783 +:1008D000070013070712B7050500905D7D8E75D2DE +:1008E000370606005C4A7D771307F73FF98FD58FEF +:1008F00041115CCA02C613073006B2476359F700BC +:10090000014541018280856693860680C9B7B2475A +:1009100085073EC6DDB791476307F50263EAA70086 +:100920008547630AF50489476309F5040545828014 +:10093000A147E31DF5FE0947094501A8FD1781EF11 +:10094000C8D20D45828005470D45B7A7070093879C +:100950000712B7060500905E798E6DD28A05C98DA3 +:100960004111CCD202C613073006B247635AF700D2 +:100970000145410182801147C9BF21470145F1B7B7 +:10098000B24785073EC6D5B70547AA876305E50286 +:1009900009476302E506054591EBB70606009C4A48 +:1009A0007D771307F73FF98F9CCA014582807D1739 +:1009B00019EB0D4582809306004037A70700130707 +:1009C0000712B7050500905D7D8E75D237070600CA +:1009D0001C4B7D761306F63FF18FD58F1CCB8547D8 +:1009E0001CCF411102C613073006B2476359F70006 +:1009F0000145410182808566938606807DBFB247AE +:100A000085073EC6DDB711C98547630DF50205456B +:100A10008280FD1791EB0D4582800946B7A707003C +:100A200093870712B7060500985E718F7DD3411139 +:100A3000C8D602C613073006B2476357F700014510 +:100A4000410182800546D9BFB24785073EC6EDB752 +:100A5000011126CAB7040600DC4806CE22CC4AC8DB +:100A60004EC652C456C2F19BDCC89C482A89C84570 +:100A7000F19B9CC883C7C5012E848A07DCC883C745 +:100A8000D5018A079CC8193D0C44AA8A0345440035 +:100A900059351848B70705002A8A98C3584808509E +:100AA000D8C3184C98C7CD35AA894850A93F834769 +:100AB00004002A8793F6170089E6D44893E62600B7 +:100AC000D4C893F6270099E637060600544A93E601 +:100AD000160054CA93F6470099E637060600144AF2 +:100AE00093E6260014CAA18B99E7B70606009C4A34 +:100AF00093E717009CCAF240624423205901232245 +:100B00004901232439012326E900D244B249224A6B +:100B1000924A4A85424905618280011106CE22CC63 +:100B200002C402C62147B707050037550800D8C7D9 +:100B300005448D478A85130505803EC022C2292AB7 +:100B4000375508009307C0038A851305058022C224 +:100B500022C43EC01122F2406244056182804111EC +:100B600022C406C62A84553F18405C4F93E707010C +:100B70005CCF1C441CCB5C4085CB1C43B7061000EB +:100B8000D58F1CC3144C5C48B240D606CE07D58F17 +:100B900083460401C206D58F8346C4012244E2067F +:100BA000D58F1CCF410182801C43B706F0FFFD1694 +:100BB000F58FC1BF032305002A8E0325C3011365EA +:100BC0000502232EA3002324C30013962601498285 +:100BD0004D8E232603012322C3001396050163547F +:100BE000060299C20545B1CB01476346D700639C15 +:100BF00008020D4582803386E7000346060005079C +:100C0000230AC300DDB799C2054505CB8147E3D070 +:100C1000D7FE03260E00034546013306F70085077D +:100C20002300A600EDB783270E00FD18DC4F93F7D5 +:100C30000702D5DF11656D8D11E18280B7070700CE +:100C400083C7470113F585001D8D3335A0008280D1 +:100C50001C414147D8CF8280B7470800938707409F +:100C60002A886304F508B757080093870780630450 +:100C7000F50A37470800630DE50A05458280331EF3 +:100C80001F013376DE0129C683A3450088431393F1 +:100C9000180033966F001346F6FF13F43300718D7E +:100CA00033146400418D88C3638B5302638C030249 +:100CB000084303AEC500718D331E6E003365C50158 +:100CC00008C38842698E884533156500498E90C2F5 +:100CD000850833D51E0145F5324441018280232623 +:100CE000C801F9B72324C801E1B7B71605003717C3 +:100CF0000500B7170500938646C1130707C1938700 +:100D0000C7C083AE05008148054F8D4F914233D552 +:100D10001E0105ED8280B716050037170500B717CD +:100D20000500938606C21307C7C1938787C1D1BF49 +:100D3000B716050037170500B7170500938686C05C +:100D4000130747C0938707C06DBF331E1F0133765B +:100D5000DE0119E2850865BF411122C635B7E16899 +:100D60009388086A01488147014781460146B705D3 +:100D7000200689B5011106CEA307010089476393B8 +:100D8000F502B7052035E1681307F1009388086A7A +:100D90000148814785460146313DF2400345F10057 +:100DA00005618280B7052005F9BF011106CE22CC6E +:100DB00026CA2306B100AA84A306C1004D37E16804 +:100DC0009388086A01487C00014789460146B78537 +:100DD000200126850964F93B130414717D1419E47C +:100DE0000D45F2406244D2440561828085452685E6 +:100DF0005137058965F50145EDB7011106CE22CCC5 +:100E000026CA2E844AC8AA84328936C6893FB24788 +:100E1000E16822869388086A01480147CA86B78537 +:100E200080022685616479331304146A7D1411C429 +:100E3000854526858137058975F9F2406244D2449B +:100E4000424905618280011106CE22CC26CA2EC6F7 +:100E5000AA8431373246E1689388086A014881479D +:100E600001478146B705802026856164A1331304BC +:100E7000146A7D1411C485452685ED3D058975F9F3 +:100E8000F2406244D24405618280B3C7A5008D8BD5 +:100E9000B308C500B1E78D4763F4C7049377350005 +:100EA0002A87B9EB13F6C8FFB306E64093070002A2 +:100EB00063C8D706AE86BA876371C70203A8060067 +:100EC0009107910623AE07FFE3EAC7FE9307F6FFFB +:100ED000998FF19B91073E97BE95636617018280BB +:100EE0002A87637E150383C7050005078505A30FC1 +:100EF000F7FEE39AE8FE828083C605000507937734 +:100F00003700A30FD7FE8505D1DF83C6050005078F +:100F100093773700A30FD7FE8505F9FF61B782806D +:100F2000411122C61304000283A3050083A24500D9 +:100F300083AF850003AFC50083AE050103AE450155 +:100F400003A3850103A8C501945113074702B30702 +:100F5000E640232E77FC232057FE2322F7FF23248D +:100F6000E7FF2326D7FF2328C7FF232A67FE232C6A +:100F700007FF232ED7FE93854502E347F4FAAE869A +:100F8000BA876371C70203A806009107910623AED2 +:100F900007FFE3EAC7FE9307F6FF998FF19B9107DE +:100FA0003E97BE956365170132444101828083C735 +:100FB000050005078505A30FF7FEE387E8FE83C755 +:100FC000050005078505A30FF7FEE392E8FEE9BFDC +:100FD00020000000010000000300000006000000E7 +:100FE000EB00000000000000000000000000000016 +:100FF000000000800000000000000700000000006A +:1010000000000000000000000000000000000000E0 +:1010100000000000000000000000000000000000D0 :0400000501000000F6 :00000001FF diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 0ee296f..284d8fc 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -5,7 +5,7 @@ #include "HardwareSerial.h" #include #include "mik32_hal_irq.h" - +#include "wiring_LL.h" // HardwareSerial class objects for use in Arduino IDE HardwareSerial Serial(0); @@ -127,9 +127,9 @@ void HardwareSerial::rx_complete_irq(void) unsigned char c; // while there is something to receive, put the data into the buffer // and edit the buffer index - while (!UART_IsRxFifoEmpty(uart)) + while(!UART_IS_RX_FIFO_EMPTY(uart)) { - c = UART_ReadByte(uart); + c = UART_READ_BYTE(uart); if (i != _rx_buffer_tail) { // write if there is space in the buffer @@ -140,7 +140,7 @@ void HardwareSerial::rx_complete_irq(void) } // wrapper for use in С-files -extern "C" void serial_handler_wrapper(uint8_t uartNumInt) +extern "C" void __attribute__((optimize("O3"))) serial_interrupt_handler(uint8_t uartNumInt) { if (uartNumInt == 0) { diff --git a/cores/arduino/HardwareSerial.h b/cores/arduino/HardwareSerial.h index d763cd6..d7f60a1 100644 --- a/cores/arduino/HardwareSerial.h +++ b/cores/arduino/HardwareSerial.h @@ -100,7 +100,7 @@ class HardwareSerial : public Stream using Print::write; // pull in write(str) operator bool() { return isInited; } - void rx_complete_irq(void); + inline void rx_complete_irq(void) __attribute__((always_inline, optimize("O3"))); }; extern HardwareSerial Serial; diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp index 4977133..8dba4e1 100644 --- a/cores/arduino/Tone.cpp +++ b/cores/arduino/Tone.cpp @@ -3,6 +3,7 @@ #include "mik32_hal_timer16.h" #include "mik32_hal_irq.h" +#include "wiring_LL.h" #define PRESCALERS_QTY 7 typedef struct @@ -100,7 +101,7 @@ void tone(uint8_t pin, unsigned int frequency, unsigned long duration) HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_1_MASK); pinMode(pin, OUTPUT); - timer_pin_port->CLEAR = timer_pin_mask; + GPIO_CLEAR_PIN((GPIO_TypeDef *)timer_pin_port, timer_pin_mask); HAL_Timer16_Counter_Start_IT(&htimer16_1, frequencyParams.period_ticks); timerIsOn = true; @@ -124,9 +125,10 @@ void noTone(uint8_t pin) { if (timerIsOn) { - timer_pin_port->CLEAR = timer_pin_mask; // pin to 0 - htimer16_1.Instance->CR &= ~TIMER16_CR_ENABLE_M; // disable timer - HAL_EPIC_MaskLevelClear(HAL_EPIC_TIMER16_1_MASK); + // pin to 0 + GPIO_CLEAR_PIN((GPIO_TypeDef *)timer_pin_port, timer_pin_mask); + TIM16_DISABLE(htimer16_1); + EPIC_LEVEL_CLEAR_BY_MASK(HAL_EPIC_TIMER16_1_MASK); pinMode(pin, INPUT); // deinit pin timer_pin = -1; // reset to default timerIsOn = false; @@ -134,25 +136,26 @@ void noTone(uint8_t pin) } // irq handler -extern "C" void tone_interrupt_handler(void) +extern "C" void __attribute__((noinline, section(".ram_text"), optimize("O3"))) tone_interrupt_handler(void) { - if ((htimer16_1.Instance->ISR & htimer16_1.Instance->IER) & TIMER16_ISR_ARR_MATCH_M) + if (TIM16_GET_ARRM_INT_STATUS(htimer16_1)) { // timer period has passed, change the pin state if (timer_toggle_count != 0) { - timer_pin_port->OUTPUT_ ^= timer_pin_mask; - + GPIO_TOGGLE_PIN((GPIO_TypeDef*)timer_pin_port, timer_pin_mask); if (timer_toggle_count > 0) timer_toggle_count--; } else { // turn off if the specified duration has passed - timer_pin_port->CLEAR = timer_pin_mask; - noTone(timer_pin); + GPIO_CLEAR_PIN((GPIO_TypeDef *)timer_pin_port, timer_pin_mask); + TIM16_DISABLE(htimer16_1); + EPIC_LEVEL_CLEAR_BY_MASK(HAL_EPIC_TIMER16_1_MASK); + timerIsOn = false; } } // reset timer interrupt flags - htimer16_1.Instance->ICR = 0xFFFFFFFF; + TIM16_CLEAR_INT_MASK(htimer16_1, 0xFFFFFFFF); } \ No newline at end of file diff --git a/cores/arduino/WInterrupts.c b/cores/arduino/WInterrupts.c index d69071f..93a9353 100644 --- a/cores/arduino/WInterrupts.c +++ b/cores/arduino/WInterrupts.c @@ -4,8 +4,7 @@ #include "pins_arduino.h" #include "wiring_digital.h" #include "WInterrupts.h" - -extern void ErrorMsgHandler(const char * msg); +#include "wiring_LL.h" typedef void (*voidFuncPtr)(void); @@ -18,13 +17,13 @@ static void nothing(void) // enable global interrupts void interrupts(void) { - HAL_IRQ_EnableInterrupts(); + GLOBAL_IRQ_ENABLE(); } // disable global interrupts void noInterrupts(void) { - HAL_IRQ_DisableInterrupts(); + GLOBAL_IRQ_DISABLE(); } // we can provide no more than 8 interrupts on gpio at the same time @@ -100,33 +99,26 @@ void detachInterrupt(uint8_t interruptNum) void disableInterrupt(uint8_t interruptNum) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) - { - int irq_line_num = interruptToGpioIntLine(interruptNum) >> GPIO_IRQ_LINE_S; // disable gpio interrupt line - GPIO_IRQ->ENABLE_CLEAR = (1 << irq_line_num); - } + GPIO_IRQ_LINE_DISABLE(interruptToGpioIntLine(interruptNum)); } // enable single interrupt void enableInterrupt(uint8_t interruptNum) { if(interruptNum < EXTERNAL_NUM_INTERRUPTS) - { - int irq_line_num = interruptToGpioIntLine(interruptNum) >> GPIO_IRQ_LINE_S; // enable gpio interrupt line - GPIO_IRQ->ENABLE_SET = (1 << irq_line_num); - } + GPIO_IRQ_LINE_ENABLE(interruptToGpioIntLine(interruptNum)); } // common gpio interrupt handler -void gpio_interrupts_handler(void) +void __attribute__((noinline, section(".ram_text"), optimize("O3"))) gpio_interrupt_handler(void) { // go through all the interrupts and call the handler for the triggered line for (uint8_t i = 0; i < EXTERNAL_NUM_INTERRUPTS; i++) { - if (HAL_GPIO_LineInterruptState(interruptToGpioIntLine(i))) + if (GPIO_IRQ_LINE_STATE(interruptToGpioIntLine(i))) intFunc[i](); } - - HAL_GPIO_ClearInterrupts(); + GPIO_IRQ_CLEAR_ALL(); } \ No newline at end of file diff --git a/cores/arduino/WString.cpp b/cores/arduino/WString.cpp index eb17895..401c657 100644 --- a/cores/arduino/WString.cpp +++ b/cores/arduino/WString.cpp @@ -19,6 +19,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#pragma GCC diagnostic ignored "-Wrestrict" // for GCC and Clang + #include "WString.h" #include "itoa.h" diff --git a/cores/arduino/board.cpp b/cores/arduino/board.cpp index bf1dc01..876cb87 100644 --- a/cores/arduino/board.cpp +++ b/cores/arduino/board.cpp @@ -1,11 +1,15 @@ #include "board.h" #include "mik32_hal_pcc.h" +#include "mik32_hal_irq.h" #include "Arduino.h" // --------------------- init --------------------- // // called before setup() void pre_init(void) { + // set irq vector to ram region + write_csr(mtvec, 0x02000000); + HAL_Init(); // gpio clock @@ -26,11 +30,15 @@ void post_init(void) } // --------------------- other --------------------- // -// print text if Serial is enabled +volatile bool use_error_messages = true; +// print error message to Serial extern "C" void ErrorMsgHandler(const char * msg) { - if(Serial) - Serial.println(msg); + // function works if Serial is used in sketch and user doesn't turn it off + #ifdef HardwareSerial_h + if(use_error_messages&&Serial) + Serial.println(msg); + #endif } diff --git a/cores/arduino/board.h b/cores/arduino/board.h index ad1ae06..61599a0 100644 --- a/cores/arduino/board.h +++ b/cores/arduino/board.h @@ -1,6 +1,10 @@ #ifndef _BOARD_H_ #define _BOARD_H_ +extern volatile bool use_error_messages; +#define DISABLE_ERROR_MESSAGES() (use_error_messages = false) +#define ENABLE_ERROR_MESSAGES() (use_error_messages = true) + // functions for init called before and after setup() void pre_init(void) ; void post_init(void); diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_eeprom.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_eeprom.h index 86f8836..4a9b584 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_eeprom.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_eeprom.h @@ -7,7 +7,7 @@ extern "C" { #include "mik32_hal_def.h" #include -#include +#include #include #ifndef HAL_EEPROM_TIMEOUT diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_gpio.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_gpio.h index b3a6776..bc3237d 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_gpio.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_gpio.h @@ -375,14 +375,96 @@ typedef enum __HAL_GPIO_InterruptMode HAL_StatusTypeDef HAL_GPIO_Init(GPIO_TypeDef *GPIO_x, GPIO_InitTypeDef *GPIO_Init); HAL_StatusTypeDef HAL_GPIO_PinConfig(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin, HAL_GPIO_ModeTypeDef mode, HAL_GPIO_PullTypeDef pull, HAL_GPIO_DSTypeDef driveStrength); -GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin); -void HAL_GPIO_WritePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin, GPIO_PinState pinState); -void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin); HAL_StatusTypeDef HAL_GPIO_InitInterruptLine(HAL_GPIO_Line_Config mux, HAL_GPIO_InterruptMode mode); HAL_StatusTypeDef HAL_GPIO_DeInitInterruptLine(HAL_GPIO_Line irqLine); -uint32_t HAL_GPIO_LineInterruptState(HAL_GPIO_Line irqLine); -GPIO_PinState HAL_GPIO_LinePinState(HAL_GPIO_Line irqLine); -void HAL_GPIO_ClearInterrupts(); + +/** + * @brief Считать текущее состояние выводов порта GPIO_x. + * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). + * @param pin маска выводов порта GPIO_x, с которых считывание значение. + * @return @ref GPIO_PIN_HIGH если с одного или больше выводов, указанных в pin, считалась 1. Иначе @ref GPIO_PIN_LOW. + */ +static inline __attribute__((always_inline)) GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin) +{ + if ((GPIO_x->SET & pin) != (uint32_t)GPIO_PIN_LOW) + return GPIO_PIN_HIGH; + else + return GPIO_PIN_LOW; +} + +/** + * @brief Задать логический уровень выходного сигнала для указанных выводов порта GPIO_x. + * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). + * @param pin маска выводов порта GPIO_x, к которым применяются указанные настройки. + * @param pinState значение состояние вывода, в которое будут установлены указанные выводы. + * Этот параметр должен быть одним из значений: + * - @ref GPIO_PIN_LOW низкий выходной уровень + * - @ref GPIO_PIN_HIGH высокий выходной уровень + */ +static inline __attribute__((always_inline)) void HAL_GPIO_WritePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin, GPIO_PinState pinState) +{ + if (pinState == GPIO_PIN_LOW) + GPIO_x->CLEAR = pin; + else + GPIO_x->SET = pin; +} + +/** + * @brief Переключить логический уровень выходного сигнала для указанных выводов порта GPIO_x. + * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). + * @param pin маска выводов порта GPIO_x, к которым применяются указанные настройки. + */ +static inline __attribute__((always_inline)) void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin) +{ + GPIO_x->OUTPUT_ ^= pin; +} + +/** + * @brief Возвращает направление вывода при работе в режиме вход/выход + * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). + * @param pin маска вывода порта GPIO_x, направление которого необходимо узнать. + * @return Возвращает направление вывода - input или output. + */ +static inline __attribute__((always_inline)) HAL_GPIO_ModeTypeDef HAL_GPIO_GetPinDirection(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin) +{ + return (GPIO_x->DIRECTION_IN & pin) == 0 ? HAL_GPIO_MODE_GPIO_OUTPUT : HAL_GPIO_MODE_GPIO_INPUT; +} + +/** + * @brief Получить состояние линии прерывания. + * @param irqLine номер линии прерывания. + * @return Возвращает 1 если сработало прерывание данной линии, иначе 0. + */ +static inline __attribute__((always_inline)) uint32_t HAL_GPIO_LineInterruptState(HAL_GPIO_Line irqLine) +{ + int irq_line_num = irqLine >> GPIO_IRQ_LINE_S; + return (GPIO_IRQ->INTERRUPT & (1 << (irq_line_num))) != 0; +} + +/** + * @brief Функция чтения логического уровня вывода, подключенного к линии прерывания. + * @param irqLine номер линии прерывания. + * @return Логический уровень вывода. + */ +static inline __attribute__((always_inline)) GPIO_PinState HAL_GPIO_LinePinState(HAL_GPIO_Line irqLine) +{ + int irq_line_num = irqLine >> GPIO_IRQ_LINE_S; + return (GPIO_PinState)((GPIO_IRQ->STATE & (1 << (irq_line_num))) >> irq_line_num); +} + +/** + * @brief Функция сброса регистра состояния прерываний. + * @note Когда срабатывает прерывание на одной из линии, в регистре INTERRUPT + * выставляется 1 в разряде, соответствующем линии прерывания. + * После обработки прерывания необходимо сбросить данный регистр + * в обработчике прерывания trap_handler(). + * Если после обработки прерывания регистр не был сброшен, + * обработчик будет вызван снова, программа будет бесконечно вызывать обработчик. + */ +static inline __attribute__((always_inline)) void HAL_GPIO_ClearInterrupts() +{ + GPIO_IRQ->CLEAR = 0b11111111; +} #ifdef __cplusplus } diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_i2c.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_i2c.h index 68a5a6c..547f48b 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_i2c.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_i2c.h @@ -380,9 +380,6 @@ typedef struct void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c); -void HAL_I2C_Disable(I2C_HandleTypeDef *hi2c); -void HAL_I2C_Reset(I2C_HandleTypeDef *hi2c); -void HAL_I2C_Enable(I2C_HandleTypeDef *hi2c); void HAL_I2C_AnalogFilterInit(I2C_HandleTypeDef *hi2c, HAL_I2C_AnalogFilterTypeDef AnalogFilter); void HAL_I2C_DigitalFilterInit(I2C_HandleTypeDef *hi2c, HAL_I2C_DigitalFilterTypeDef DigitalFilter); void HAL_I2C_SetClockSpeed(I2C_HandleTypeDef *hi2c); @@ -394,6 +391,7 @@ void HAL_I2C_SBCMode(I2C_HandleTypeDef *hi2c, HAL_I2C_SBCModeTypeDef SBCMode); void HAL_I2C_SlaveInit(I2C_HandleTypeDef *hi2c); void HAL_I2C_MasterInit(I2C_HandleTypeDef *hi2c); HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c); +HAL_StatusTypeDef HAL_I2C_Deinit(I2C_HandleTypeDef *hi2c); void HAL_I2C_AutoEnd(I2C_HandleTypeDef *hi2c, HAL_I2C_AutoEndModeTypeDef AutoEnd); HAL_StatusTypeDef HAL_I2C_Master_WaitTXIS(I2C_HandleTypeDef *hi2c, uint32_t Timeout); HAL_StatusTypeDef HAL_I2C_Master_WaitRXNE(I2C_HandleTypeDef *hi2c, uint32_t Timeout); @@ -427,6 +425,60 @@ HAL_StatusTypeDef HAL_I2C_Slave_Receive_IT(I2C_HandleTypeDef *hi2c, uint8_t *pDa HAL_StatusTypeDef HAL_I2C_Slave_Receive_NOSTRETCH_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t DataSize); HAL_StatusTypeDef HAL_I2C_Slave_ReceiveSBC_IT(I2C_HandleTypeDef *hi2c, uint8_t *pData, uint16_t DataSize); + +static inline __attribute__((always_inline)) void HAL_I2C_Disable(I2C_HandleTypeDef *hi2c) +{ + hi2c->Instance->CR1 &= ~I2C_CR1_PE_M; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Reset(I2C_HandleTypeDef *hi2c) +{ + hi2c->ErrorCode = I2C_ERROR_NONE; + + hi2c->Instance->CR1 &= ~I2C_CR1_PE_M; + hi2c->Instance->CR1 |= I2C_CR1_PE_M; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Enable(I2C_HandleTypeDef *hi2c) +{ + hi2c->Instance->CR1 |= I2C_CR1_PE_M; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Reset_Interrupt_Flag(I2C_HandleTypeDef *hi2c, uint32_t mask) +{ + hi2c->Instance->ICR |= mask; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Reset_TXDR_Content(I2C_HandleTypeDef *hi2c) +{ + hi2c->Instance->ISR |= I2C_ISR_TXE_M; +} + +static inline __attribute__((always_inline)) uint32_t HAL_I2C_Get_Interrupts_Status(I2C_HandleTypeDef *hi2c) +{ + return hi2c->Instance->ISR; +} + +static inline __attribute__((always_inline)) uint32_t HAL_I2C_Get_CR1_Content(I2C_HandleTypeDef *hi2c) +{ + return hi2c->Instance->CR1; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Write_TXDR(I2C_HandleTypeDef *hi2c, uint8_t value) +{ + hi2c->Instance->TXDR = value; +} + +static inline __attribute__((always_inline)) uint8_t HAL_I2C_Get_RXDR(I2C_HandleTypeDef *hi2c) +{ + return hi2c->Instance->RXDR; +} + +static inline __attribute__((always_inline)) void HAL_I2C_Clear_Reload(I2C_HandleTypeDef *hi2c) +{ + hi2c->Instance->CR2 &= ~I2C_CR2_RELOAD_M; +} + static inline __attribute__((always_inline)) void HAL_I2C_ADDR_IRQ(I2C_HandleTypeDef *hi2c) { if (hi2c->Instance->CR1 & I2C_CR1_SBC_M) diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_spi.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_spi.h index f95a8ec..e24e7df 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_spi.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_spi.h @@ -253,7 +253,8 @@ void HAL_SPI_CS_Disable(SPI_HandleTypeDef *hspi); HAL_StatusTypeDef HAL_SPI_Exchange(SPI_HandleTypeDef *hspi, uint8_t TransmitBytes[], uint8_t ReceiveBytes[], uint32_t Size, uint32_t Timeout); HAL_StatusTypeDef HAL_SPI_ExchangeThreshold(SPI_HandleTypeDef *hspi, uint8_t TransmitBytes[], uint8_t ReceiveBytes[], uint32_t DataSize, uint32_t Timeout); HAL_StatusTypeDef HAL_SPI_Exchange_IT(SPI_HandleTypeDef *hspi, uint8_t TransmitBytes[], uint8_t ReceiveBytes[], uint32_t Size); - +void HAL_SPI_Set_Clock_Divider(SPI_HandleTypeDef *hspi); +void HAL_SPI_Set_Clock_Mode(SPI_HandleTypeDef *hspi); /** * @brief Разрешить прерывания в соответствии с маской. diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer16.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer16.h index 873a26f..60dcc2c 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer16.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer16.h @@ -230,8 +230,6 @@ typedef struct __Timer16_HandleTypeDef void HAL_TIMER16_MspInit(Timer16_HandleTypeDef* htimer16); -void HAL_Timer16_Disable(Timer16_HandleTypeDef *htimer16); -void HAL_Timer16_Enable(Timer16_HandleTypeDef *htimer16); void HAL_Timer16_SetActiveEdge(Timer16_HandleTypeDef *htimer16, uint8_t ActiveEdge); void HAL_Timer16_SetSourceClock(Timer16_HandleTypeDef *htimer16, uint8_t SourceClock); void HAL_Timer16_SetCountMode(Timer16_HandleTypeDef *htimer16, uint8_t CountMode); @@ -278,6 +276,26 @@ void HAL_Timer16_SetInterruptARRM(Timer16_HandleTypeDef *htimer16); void HAL_Timer16_SetInterruptCMPM(Timer16_HandleTypeDef *htimer16); void HAL_Timer16_InterruptInit(Timer16_HandleTypeDef *htimer16); +/** + * @brief Выключить таймер. + * Может использоваться для отключения таймера или при записи в регистр CFGR. + * + * @param htimer16 Указатель на структуру с настройками Timer16. + */ +static inline void __attribute__((always_inline)) HAL_Timer16_Disable(Timer16_HandleTypeDef *htimer16) +{ + htimer16->Instance->CR &= ~TIMER16_CR_ENABLE_M; +} + +/** + * @brief Включить таймер + * @param htimer16 Указатель на структуру с настройками Timer16. + */ +static inline void __attribute__((always_inline)) HAL_Timer16_Enable(Timer16_HandleTypeDef *htimer16) +{ + htimer16->Instance->CR |= TIMER16_CR_ENABLE_M; +} + /** * @brief Получить статус прерываний Timer16. * Функция возвращает статус прерываний в соответствии с маской разрешенный прерываний. @@ -291,6 +309,16 @@ static inline __attribute__((always_inline)) uint32_t HAL_Timer16_GetInterruptSt return interrupt_status; } +/** + * @brief Получить статус прерывания по соответствию автозагрузке ARRM + * @param htimer16 Указатель на структуру с настройками Timer16 + * @return Статус прерывания ARRM - true, если прерывание сработало + */ +static inline __attribute__((always_inline)) bool HAL_Timer16_GetInterruptStatus_ARRM(Timer16_HandleTypeDef *htimer16) +{ + return (bool)((htimer16->Instance->ISR & htimer16->Instance->IER) & TIMER16_ISR_ARR_MATCH_M); +} + /** * @brief Очистить флаг прерывания. * @param htimer16 Указатель на структуру с настройками Timer16. diff --git a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer32.h b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer32.h index 9b3f67e..e044dfd 100644 --- a/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer32.h +++ b/cores/arduino/mik32/hal/peripherals/Include/mik32_hal_timer32.h @@ -226,6 +226,7 @@ static inline __attribute__((always_inline)) HAL_StatusTypeDef HAL_Timer32_WaitF void HAL_TIMER32_MspInit(TIMER32_HandleTypeDef* htimer32); void HAL_TIMER32_Channel_MspInit(TIMER32_CHANNEL_HandleTypeDef* timerChannel); HAL_StatusTypeDef HAL_Timer32_Init(TIMER32_HandleTypeDef *timer); +HAL_StatusTypeDef HAL_Timer32_Deinit(TIMER32_HandleTypeDef *timer); void HAL_Timer32_State_Set(TIMER32_HandleTypeDef *timer, HAL_TIMER32_StateTypeDef state); void HAL_Timer32_Top_Set(TIMER32_HandleTypeDef *timer, uint32_t top); void HAL_Timer32_Prescaler_Set(TIMER32_HandleTypeDef *timer, uint32_t prescaler); diff --git a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_gpio.c b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_gpio.c index ea3e1ae..ce6ae55 100644 --- a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_gpio.c +++ b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_gpio.c @@ -93,58 +93,6 @@ HAL_StatusTypeDef HAL_GPIO_PinConfig(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin, return HAL_GPIO_Init(GPIO_x, &GPIO_InitStruct); } -/** - * @brief Считать текущее состояние выводов порта GPIO_x. - * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). - * @param pin маска выводов порта GPIO_x, с которых считывание значение. - * @return @ref GPIO_PIN_HIGH если с одного или больше выводов, указанных в pin, считалась 1. Иначе @ref GPIO_PIN_LOW. - */ -GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin) -{ - GPIO_PinState bitStatus; - - if ((GPIO_x->SET & pin) != (uint32_t)GPIO_PIN_LOW) - { - bitStatus = GPIO_PIN_HIGH; - } - else - { - bitStatus = GPIO_PIN_LOW; - } - return bitStatus; -} - -/** - * @brief Задать логический уровень выходного сигнала для указанных выводов порта GPIO_x. - * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). - * @param pin маска выводов порта GPIO_x, к которым применяются указанные настройки. - * @param pinState значение состояние вывода, в которое будут установлены указанные выводы. - * Этот параметр должен быть одним из значений: - * - @ref GPIO_PIN_LOW низкий выходной уровень - * - @ref GPIO_PIN_HIGH высокий выходной уровень - */ -void HAL_GPIO_WritePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin, GPIO_PinState pinState) -{ - if (pinState == GPIO_PIN_LOW) - { - GPIO_x->CLEAR = pin; - } - else - { - GPIO_x->SET = pin; - } -} - -/** - * @brief Переключить логический уровень выходного сигнала для указанных выводов порта GPIO_x. - * @param GPIO_x порт GPIO_x, где x может быть (0, 1, 2). - * @param pin маска выводов порта GPIO_x, к которым применяются указанные настройки. - */ -void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIO_x, HAL_PinsTypeDef pin) -{ - GPIO_x->OUTPUT_ ^= pin; -} - /** * @brief Функция инициализации линии прерывания. * \param mux настройка мультиплексора линии прерывания. @@ -222,39 +170,3 @@ HAL_StatusTypeDef HAL_GPIO_DeInitInterruptLine(HAL_GPIO_Line irqLine) return HAL_OK; } - -/** - * @brief Получить состояние линии прерывания. - * @param irqLine номер линии прерывания. - * @return Возвращает 1 если сработало прерывание данной линии, иначе 0. - */ -uint32_t HAL_GPIO_LineInterruptState(HAL_GPIO_Line irqLine) -{ - int irq_line_num = irqLine >> GPIO_IRQ_LINE_S; - return (GPIO_IRQ->INTERRUPT & (1 << (irq_line_num))) != 0; -} - -/** - * @brief Функция чтения логического уровня вывода, подключенного к линии прерывания. - * @param irqLine номер линии прерывания. - * @return Логический уровень вывода. - */ -GPIO_PinState HAL_GPIO_LinePinState(HAL_GPIO_Line irqLine) -{ - int irq_line_num = irqLine >> GPIO_IRQ_LINE_S; - return (GPIO_PinState)((GPIO_IRQ->STATE & (1 << (irq_line_num))) >> irq_line_num); -} - -/** - * @brief Функция сброса регистра состояния прерываний. - * @note Когда срабатывает прерывание на одной из линии, в регистре INTERRUPT - * выставляется 1 в разряде, соответствующем линии прерывания. - * После обработки прерывания необходимо сбросить данный регистр - * в обработчике прерывания trap_handler(). - * Если после обработки прерывания регистр не был сброшен, - * обработчик будет вызван снова, программа будет бесконечно вызывать обработчик. - */ -void HAL_GPIO_ClearInterrupts() -{ - GPIO_IRQ->CLEAR = 0b11111111; -} diff --git a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_i2c.c b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_i2c.c index 2a2cd16..f44138a 100644 --- a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_i2c.c +++ b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_i2c.c @@ -26,24 +26,6 @@ __attribute__((weak)) void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) } -void HAL_I2C_Disable(I2C_HandleTypeDef *hi2c) -{ - hi2c->Instance->CR1 &= ~I2C_CR1_PE_M; -} - -void HAL_I2C_Reset(I2C_HandleTypeDef *hi2c) -{ - hi2c->ErrorCode = I2C_ERROR_NONE; - - hi2c->Instance->CR1 &= ~I2C_CR1_PE_M; - hi2c->Instance->CR1 |= I2C_CR1_PE_M; -} - -void HAL_I2C_Enable(I2C_HandleTypeDef *hi2c) -{ - hi2c->Instance->CR1 |= I2C_CR1_PE_M; -} - void HAL_I2C_AnalogFilterInit(I2C_HandleTypeDef *hi2c, HAL_I2C_AnalogFilterTypeDef AnalogFilter) { hi2c->Init.AnalogFilter = AnalogFilter; @@ -244,6 +226,24 @@ HAL_StatusTypeDef HAL_I2C_Init(I2C_HandleTypeDef *hi2c) return HAL_OK; } +HAL_StatusTypeDef HAL_I2C_Deinit(I2C_HandleTypeDef *hi2c) +{ + HAL_I2C_Disable(hi2c); + + if (hi2c->Instance == I2C_0) + { + __HAL_PCC_I2C_0_CLK_DISABLE(); + HAL_GPIO_PinConfig(GPIO_0, GPIO_PIN_9 | GPIO_PIN_10, HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + } + else if(hi2c->Instance == I2C_1) + { + __HAL_PCC_I2C_1_CLK_DISABLE(); + HAL_GPIO_PinConfig(GPIO_1, GPIO_PIN_12 | GPIO_PIN_13, HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + } + + return HAL_OK; +} + void HAL_I2C_AutoEnd(I2C_HandleTypeDef *hi2c, HAL_I2C_AutoEndModeTypeDef AutoEnd) { hi2c->Instance->CR2 &= ~I2C_CR2_AUTOEND_M; diff --git a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_spi.c b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_spi.c index 3c07a13..41da68c 100644 --- a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_spi.c +++ b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_spi.c @@ -282,6 +282,37 @@ HAL_StatusTypeDef HAL_SPI_Init(SPI_HandleTypeDef *hspi) return error_code; } +/** + * @brief Установить новый делитель частоты + * + * @param hspi указатель на структуру SPI_HandleTypeDef, которая содержит + * информацию о конфигурации для модуля SPI. + */ +void HAL_SPI_Set_Clock_Divider(SPI_HandleTypeDef *hspi) +{ + uint32_t config = hspi->Instance->CONFIG; + HAL_SPI_Disable(hspi); + config &= ~(0b111 << SPI_CONFIG_BAUD_RATE_DIV_S); // очистить + config |= (hspi->Init.BaudRateDiv << SPI_CONFIG_BAUD_RATE_DIV_S); // установить новое + hspi->Instance->CONFIG = config; // сохранить конфигурацию +} + +/** + * @brief Установить новый режим работы + * + * @param hspi указатель на структуру SPI_HandleTypeDef, которая содержит + * информацию о конфигурации для модуля SPI. + */ +void HAL_SPI_Set_Clock_Mode(SPI_HandleTypeDef *hspi) +{ + uint32_t config = hspi->Instance->CONFIG; + HAL_SPI_Disable(hspi); + config &= ~((1 << SPI_CONFIG_CLK_PH_S) | (1 << SPI_CONFIG_CLK_POL_S)); // очистить + config |= ((hspi->Init.CLKPhase << SPI_CONFIG_CLK_PH_S) | + (hspi->Init.CLKPolarity << SPI_CONFIG_CLK_POL_S)); // установить новые значения + hspi->Instance->CONFIG = config; // сохранить конфигурацию +} + /** * @brief Очистить буфер TX_FIFO. * diff --git a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer16.c b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer16.c index 7bcda80..8756bb7 100644 --- a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer16.c +++ b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer16.c @@ -99,26 +99,6 @@ __attribute__((weak)) void HAL_TIMER16_MspInit(Timer16_HandleTypeDef* htimer16) } } -/** - * @brief Выключить таймер. - * Может использоваться для отключения таймера или при записи в регистр CFGR. - * - * @param htimer16 Указатель на структуру с настройками Timer16. - */ -void HAL_Timer16_Disable(Timer16_HandleTypeDef *htimer16) -{ - htimer16->Instance->CR &= ~TIMER16_CR_ENABLE_M; -} - -/** - * @brief Включить таймер - * @param htimer16 Указатель на структуру с настройками Timer16. - */ -void HAL_Timer16_Enable(Timer16_HandleTypeDef *htimer16) -{ - htimer16->Instance->CR |= TIMER16_CR_ENABLE_M; -} - /** * @brief Установить активный фронт для подсчёта или задать подрежим энкодера. * Используется при тактировании Timer16 от внешнего источника тактового сигнала на выводе Input1. diff --git a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer32.c b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer32.c index f5b3fe0..ff715ce 100644 --- a/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer32.c +++ b/cores/arduino/mik32/hal/peripherals/Source/mik32_hal_timer32.c @@ -78,6 +78,41 @@ HAL_StatusTypeDef HAL_Timer32_Init(TIMER32_HandleTypeDef *timer) return HAL_OK; } +HAL_StatusTypeDef HAL_Timer32_Deinit(TIMER32_HandleTypeDef *timer) +{ + if ((timer->Instance != TIMER32_0) && (timer->Instance != TIMER32_1) && (timer->Instance != TIMER32_2)) + { + return HAL_ERROR; + } + + if (timer->Instance == TIMER32_0) + { + __HAL_PCC_TIMER32_0_CLK_DISABLE(); + } + + if (timer->Instance == TIMER32_1) + { + __HAL_PCC_TIMER32_1_CLK_DISABLE(); + if (timer->Clock.Source == TIMER32_SOURCE_TX_PAD) + HAL_GPIO_PinConfig(GPIO_0, GPIO_PIN_4, HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + } + + if (timer->Instance == TIMER32_2) + { + __HAL_PCC_TIMER32_2_CLK_DISABLE(); + if (timer->Clock.Source == TIMER32_SOURCE_TX_PAD) + HAL_GPIO_PinConfig(GPIO_1, GPIO_PIN_4, HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + } + + HAL_Timer32_InterruptMask_Clear(timer, 0xFFFFFFFF); + HAL_Timer32_Prescaler_Set(timer, 0); + HAL_Timer32_InterruptFlags_ClearMask(timer, 0xFFFFFFFF); + HAL_Timer32_InterruptFlags_Clear(timer); + + return HAL_OK; +} + + void HAL_Timer32_State_Set(TIMER32_HandleTypeDef *timer, HAL_TIMER32_StateTypeDef state) { timer->State = state; @@ -199,7 +234,7 @@ HAL_StatusTypeDef HAL_Timer32_Channel_Init(TIMER32_CHANNEL_HandleTypeDef *timerC return HAL_ERROR; } - HAL_TIMER32_Channel_MspInit(timerChannel); + // HAL_TIMER32_Channel_MspInit(timerChannel); // здесь инициализируются сразу все каналы выбранного таймера, а нам такого не надо timerChannel->Instance = (TIMER32_CHANNEL_TypeDef *)&(timerChannel->TimerInstance->CHANNELS[timerChannel->ChannelIndex]); @@ -220,6 +255,8 @@ HAL_StatusTypeDef HAL_Timer32_Channel_DeInit(TIMER32_CHANNEL_HandleTypeDef *time return HAL_ERROR; } + timerChannel->Instance = (TIMER32_CHANNEL_TypeDef *)&(timerChannel->TimerInstance->CHANNELS[timerChannel->ChannelIndex]); + HAL_Timer32_Channel_Disable(timerChannel); HAL_Timer32_Channel_CaptureEdge_Set(timerChannel, 0); HAL_Timer32_Channel_ICR_Clear(timerChannel); diff --git a/cores/arduino/mik32/shared/ldscripts/ram.ld b/cores/arduino/mik32/shared/ldscripts/ram.ld index 29049ce..7f9ed1e 100644 --- a/cores/arduino/mik32/shared/ldscripts/ram.ld +++ b/cores/arduino/mik32/shared/ldscripts/ram.ld @@ -10,36 +10,52 @@ MEMORY { } STACK_SIZE = 1024; +HEAP_SIZE = 1024; CL_SIZE = 16; SECTIONS { .text ORIGIN(ram) : { PROVIDE(__TEXT_START__ = .); - *crt0.o(.text .text.*) + *crt0.S.o(.text .text.*) *(.text.smallsysteminit) *(.text.SmallSystemInit) . = ORIGIN(ram) + 0xC0; - KEEP(*crt0.o(.trap_text)) + KEEP(*crt0.S.o(.trap_text)) *(.text) *(.text.*) *(.rodata) *(.rodata.*) . = ALIGN(CL_SIZE); - PROVIDE(__TEXT_END__ = .); } >ram + .init_array (READONLY) : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) + KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) + PROVIDE_HIDDEN (__init_array_end = .); + + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + PROVIDE(__TEXT_END__ = .); + } >ram AT>ram + .data : AT( __TEXT_END__ ) { PROVIDE(__DATA_START__ = .); _gp = .; *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) - *(.sdata .sdata.* .gnu.linkonce.s.*) + *(.sdata .sdata.* .sdata* .sdata*.* .gnu.linkonce.s.*) *(.data .data.*) + *(.ramfunc) . = ALIGN(CL_SIZE); - } >ram - + } >ram + __DATA_IMAGE_START__ = LOADADDR(.data); __DATA_IMAGE_END__ = LOADADDR(.data) + SIZEOF(.data); ASSERT(__DATA_IMAGE_END__ < ORIGIN(ram) + LENGTH(ram) - STACK_SIZE, "Data image overflows ram section") @@ -72,12 +88,19 @@ SECTIONS { PROVIDE(__BSS_END__ = .); } >ram + .heap : { + PROVIDE(__heap_start = .); + . += HEAP_SIZE; + . = ALIGN(CL_SIZE); + PROVIDE(__heap_end = .); + } >ram + _end = .; PROVIDE(__end = .); /* End of uninitalized data segement */ - .stack ORIGIN(ram) + LENGTH(ram) - STACK_SIZE : { + .stack ORIGIN(ram) + LENGTH(ram) - STACK_SIZE: { FILL(0); PROVIDE(__STACK_START__ = .); . += STACK_SIZE; diff --git a/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld b/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld index 8a15697..20ded45 100644 --- a/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld +++ b/cores/arduino/mik32/shared/ldscripts/spifi_cpp.ld @@ -21,8 +21,8 @@ SECTIONS { *crt0.S.o(.text .text.*) *(.text.smallsysteminit) *(.text.SmallSystemInit) - . = ORIGIN(rom) + 0xC0; - KEEP(*crt0.S.o(.trap_text)) + /*. = ORIGIN(rom) + 0xC0;*/ + /*KEEP(*crt0.S.o(.trap_text))*/ *(.text) *(.text.*) @@ -50,15 +50,17 @@ SECTIONS { AT( __TEXT_END__ ) { PROVIDE(__DATA_START__ = .); _gp = .; + . = ORIGIN(ram) + 0xC0; + KEEP(*crt0.S.o(.trap_text)) *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) *(.sdata .sdata.* .sdata* .sdata*.* .gnu.linkonce.s.*) *(.data .data.*) . = ALIGN(CL_SIZE); + PROVIDE(__DATA_END__ = .); } >ram __DATA_IMAGE_START__ = LOADADDR(.data); __DATA_IMAGE_END__ = LOADADDR(.data) + SIZEOF(.data); - ASSERT(__DATA_IMAGE_END__ < ORIGIN(rom) + LENGTH(rom), "Data image overflows rom section") /* thread-local data segment */ .tdata : { @@ -84,10 +86,26 @@ SECTIONS { .bss : { *(.bss .bss.*) + *(COMMON) . = ALIGN(CL_SIZE); PROVIDE(__BSS_END__ = .); } >ram + /* Code intended to be copied to REGION_RAM before execution */ + .ram_text : + AT( ALIGN(__DATA_IMAGE_END__, CL_SIZE) ) { + PROVIDE(__RAM_TEXT_START__ = .); + *(.ram_text) + . = ALIGN(CL_SIZE); + PROVIDE(__RAM_TEXT_END__ = .); + } > ram + + __RAM_TEXT_IMAGE_START__ = LOADADDR(.ram_text); + __RAM_TEXT_IMAGE_END__ = LOADADDR(.ram_text) + SIZEOF(.ram_text); + ASSERT(__RAM_TEXT_IMAGE_END__ < ORIGIN(rom) + LENGTH(rom), "rom segment overflows") + + ASSERT(__RAM_TEXT_END__ < ORIGIN(ram) + LENGTH(ram) - STACK_SIZE - HEAP_SIZE, "REGION_RAM section overflows") + .heap : { PROVIDE(__heap_start = .); . += HEAP_SIZE; diff --git a/cores/arduino/mik32/shared/periphery/eeprom.h b/cores/arduino/mik32/shared/periphery/eeprom_def.h similarity index 100% rename from cores/arduino/mik32/shared/periphery/eeprom.h rename to cores/arduino/mik32/shared/periphery/eeprom_def.h diff --git a/cores/arduino/mik32/shared/runtime/crt0.S b/cores/arduino/mik32/shared/runtime/crt0.S index e058b76..e4185cd 100644 --- a/cores/arduino/mik32/shared/runtime/crt0.S +++ b/cores/arduino/mik32/shared/runtime/crt0.S @@ -62,6 +62,11 @@ memset_2: _start: + li t0, 128000 + start_loop_delay: + nop + addi t0, t0, -1 + bnez t0, start_loop_delay # Init stack and global pointer # la_abs sp, __C_STACK_TOP__ @@ -73,20 +78,19 @@ _start: la_abs a2, __DATA_IMAGE_END__ la_abs a3, __DATA_START__ memcpy a1, a2, a3, t0 + + # Init ramfunc + # + la_abs a1, __RAM_TEXT_IMAGE_START__ + la_abs a2, __RAM_TEXT_IMAGE_END__ + la_abs a3, __RAM_TEXT_START__ + memcpy a1, a2, a3, t0 # Clear bss # la_abs a1, __BSS_START__ la_abs a2, __BSS_END__ memset a1, a2, zero - - #ifdef MIK32V0 - # Enable pad_config clocking - # - li t0, (1 << 3) - li t1, 0x50014 - sw t0, (t1) - #endif jalr_abs ra, SmallSystemInit jalr_abs ra, SystemInit diff --git a/cores/arduino/trap_handler.c b/cores/arduino/trap_handler.c index 5d01229..b491eae 100644 --- a/cores/arduino/trap_handler.c +++ b/cores/arduino/trap_handler.c @@ -1,37 +1,46 @@ #include "mik32_hal_irq.h" +#include "wiring_LL.h" // isr functions -extern void serial_handler_wrapper(uint8_t uartNumInt); -extern void gpio_interrupts_handler(void); +extern void serial_interrupt_handler(uint8_t uartNumInt); +extern void gpio_interrupt_handler(void); extern void tone_interrupt_handler(void); -void __attribute__((weak)) wire_handler_wrapper(void) +void __attribute__((weak)) wire_interrupt_handler(void) +{ + // dummy function for case when wire library is not in use +} +void __attribute__((weak)) servo_interrupt_handler(void) { // dummy function for case when wire library is not in use } // ---------------------------------------------- // -void trap_handler(void) +void __attribute__((noinline, section(".ram_text"), optimize("O3"))) trap_handler (void) { + // gpio interrupt + if (EPIC_CHECK_GPIO_IRQ()) + gpio_interrupt_handler(); + // tone timer interrupt if (EPIC_CHECK_TIMER16_1()) tone_interrupt_handler(); + // servo timer interrupt + if (EPIC_CHECK_TIMER16_2()) + servo_interrupt_handler(); + // uart0 interrupt if (EPIC_CHECK_UART_0()) - serial_handler_wrapper(0); + serial_interrupt_handler(0); // uart1 interrupt if (EPIC_CHECK_UART_1()) - serial_handler_wrapper(1); - - // gpio interrupt - if (EPIC_CHECK_GPIO_IRQ()) - gpio_interrupts_handler(); + serial_interrupt_handler(1); // i2c interrupt if (EPIC_CHECK_I2C_1()) - wire_handler_wrapper(); + wire_interrupt_handler(); // reset all interrupts - HAL_EPIC_Clear(0xFFFFFFFF); + EPIC_CLEAR_ALL(); } diff --git a/cores/arduino/util/delay.h b/cores/arduino/util/delay.h new file mode 100644 index 0000000..c9ccd0d --- /dev/null +++ b/cores/arduino/util/delay.h @@ -0,0 +1,6 @@ +#ifndef __DELAY_H_ +#define __DELAY_H_ + + + +#endif // __DELAY_H_ diff --git a/cores/arduino/wiring_LL.h b/cores/arduino/wiring_LL.h new file mode 100644 index 0000000..4e2723e --- /dev/null +++ b/cores/arduino/wiring_LL.h @@ -0,0 +1,65 @@ +#ifndef _WIRING_LL_H_ +#define _WIRING_LL_H_ + +#include "mik32_hal_irq.h" +#include "mik32_hal_gpio.h" +#include "mik32_hal_scr1_timer.h" +#include "mik32_hal_timer16.h" + +// ----------------- COMMON ----------------- // +// convert pin mask from HAL_PinsTypeDef to pin number +#define PIN_MASK_TO_PIN_NUMBER(mask) \ +({ \ + uint8_t pos; \ + for (pos = 0; pos < 16; pos++) \ + if ((mask >> pos) & 1) \ + break; \ + pos; \ +}) + +// ----------------- SYSTICK ----------------- // +// get ticks from systick +#define SYSTICK_GET_TICKS() ((uint64_t)(SCR1_TIMER->MTIMEH)<<32 | SCR1_TIMER->MTIME) + +// ----------------- UART ----------------- // +#define UART_READ_BYTE(pUart) ((uint16_t)pUart->RXDATA) +#define UART_IS_RX_FIFO_EMPTY(pUart) ((pUart->FLAGS & UART_FLAGS_RXNE_M) == 0) + +// ----------------- TIMER16 ----------------- // +#define TIM16_DISABLE(htim16) (htim16.Instance->CR &= ~TIMER16_CR_ENABLE_M) +#define TIM16_CLEAR_INT_MASK(htim16, intMask) (htim16.Instance->ICR = intMask) +#define TIM16_GET_ARRM_INT_STATUS(htim16) ((bool)((htim16.Instance->ISR & htim16.Instance->IER) & TIMER16_ISR_ARR_MATCH_M)) +#define TIM16_DISABLE_INT_BY_MASK(htim16,intMask) (htim16.Instance->IER &= ~(intMask)) + +// ----------------- EPIC ----------------- // +#define EPIC_LEVEL_CLEAR_BY_MASK(mask) (EPIC->MASK_LEVEL_CLEAR |= mask) +#define EPIC_CLEAR_ALL() (EPIC->CLEAR = 0xFFFFFFFF) + +// ----------------- IRQ ----------------- // +#define GLOBAL_IRQ_DISABLE() (clear_csr(mie, MIE_MEIE)) +#define GLOBAL_IRQ_ENABLE() set_csr(mstatus, MSTATUS_MIE); \ + set_csr(mie, MIE_MEIE) + +// ----------------- GPIO ----------------- // +#define GPIO_SET_PIN(GPIO_x, pinMask) ((GPIO_x)->SET = (pinMask)) +#define GPIO_CLEAR_PIN(GPIO_x, pinMask) ((GPIO_x)->CLEAR = (pinMask)) +#define GPIO_TOGGLE_PIN(GPIO_x, pinMask) ((GPIO_x)->OUTPUT_ ^= pinMask) +#define GPIO_READ_PIN(GPIO_x, pinMask) (((GPIO_x)->SET & (pinMask)) != (uint32_t)GPIO_PIN_LOW ? GPIO_PIN_HIGH : GPIO_PIN_LOW) +// get pin state by it's number +#define GPIO_GET_PIN_STATE(GPIO_x, pinNumber) (((GPIO_x)->OUTPUT_ >> pinNumber) & 0b1) + +// ----------------- GPIO IRQ ----------------- // +#define GPIO_IRQ_LINE_ENABLE(lineMask) ( GPIO_IRQ->ENABLE_SET = (1 << (lineMask >> GPIO_IRQ_LINE_S)) ) +#define GPIO_IRQ_LINE_DISABLE(lineMask) ( GPIO_IRQ->ENABLE_CLEAR = (1 << (lineMask >> GPIO_IRQ_LINE_S)) ) +#define GPIO_IRQ_LINE_STATE(lineMask) ((GPIO_IRQ->INTERRUPT & (1 << (lineMask >> GPIO_IRQ_LINE_S))) != 0) +#define GPIO_IRQ_CLEAR_ALL() ( GPIO_IRQ->CLEAR = 0b11111111) + +// ----------------- PIN CONFIG ----------------- // +// return config of pin with pinNumber(0...16) in portReg (config, pupd, ds for ports 0...2) +#define PIN_GET_PAD_CONFIG(portReg, pinNumber) ((PAD_CONFIG->portReg >> (pinNumber<<1)) & 0b11) +#define PIN_SET_PAD_CONFIG(portReg, pinNumber, value) (PAD_CONFIG->portReg = (PAD_CONFIG->portReg & (~PAD_CONFIG_PIN_M(pinNumber))) \ + | PAD_CONFIG_PIN(pinNumber, value)) + + + +#endif /* _WIRING_LL_H_ */ \ No newline at end of file diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 75449a2..c7e5c35 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -8,9 +8,13 @@ extern "C" { #include "mik32_hal_gpio.h" #include "mik32_hal_timer32.h" -extern void ErrorMsgHandler(const char * msg); // -------------------------- Analog read -------------------------- // +#define ADC_SAMPLES_QTY 10 // samples quantity for averaging adc results +#define ADC_DEFAULT_RESOLUTION 10 // resolution for arduino compatibility + +uint8_t currentResolution = ADC_DEFAULT_RESOLUTION; // resolution used for output results + // structure for ADC channel initialization. Only the channel number // changes, everything else is the same static ADC_HandleTypeDef hadc = @@ -21,6 +25,16 @@ static ADC_HandleTypeDef hadc = .Init.Sel = 0 }; +void analogReadResolution(uint8_t resolution) +{ + // resolution limits + if (resolution > 32) resolution = 32; + if (resolution < 1) resolution = 1; + + // save new resolution + currentResolution = resolution; +} + // initialize the channel, run a single measurement, wait for the result uint32_t analogRead(uint32_t PinNumber) { @@ -44,11 +58,31 @@ uint32_t analogRead(uint32_t PinNumber) hadc.Init.Sel = adcChannel; HAL_ADC_Init(&hadc); - // start the conversion twice in case another channel was polled before + // start the dummy conversion in case another channel was polled before HAL_ADC_SINGLE_AND_SET_CH(hadc.Instance, adcChannel); - value = HAL_ADC_WaitAndGetValue(&hadc); - HAL_ADC_Single(&hadc); - value = HAL_ADC_WaitAndGetValue(&hadc); + HAL_ADC_WaitAndGetValue(&hadc); + + // accumulate results + uint32_t acc = 0; + for (uint8_t i = 0; i MCU_ADC_RESOLUTION) + { + // extra least significant bits are padded with zeros + value = (value << (currentResolution - MCU_ADC_RESOLUTION)); + } + else + { + // extra least significant bits read from the ADC are discarded + value = (value >> (MCU_ADC_RESOLUTION - currentResolution)); + } } else ErrorMsgHandler("analogRead(): invalid analog pin number"); @@ -68,25 +102,8 @@ static TIMER32_HandleTypeDef htimer32; static TIMER32_CHANNEL_HandleTypeDef htimer32_channel; static uint32_t WriteValMax = WRITE_VAL_MAX_DEFAULT; static uint32_t pwmTopVal = PWM_TOP_VAL_DEFAULT; -static bool pwmIsInited = false; +static uint8_t pwmIsInited = 0; -HAL_StatusTypeDef Timer32_Channel_Init(TIMER32_CHANNEL_HandleTypeDef *timerChannel) -{ - if (timerChannel->TimerInstance == TIMER32_0) - return HAL_ERROR; - - // gpio init removed from standard function - - timerChannel->Instance = (TIMER32_CHANNEL_TypeDef *)&(timerChannel->TimerInstance->CHANNELS[timerChannel->ChannelIndex]); - HAL_Timer32_Channel_PWM_Invert_Set(timerChannel, timerChannel->PWM_Invert); - HAL_Timer32_Channel_Mode_Set(timerChannel, timerChannel->Mode); - HAL_Timer32_Channel_CaptureEdge_Set(timerChannel, timerChannel->CaptureEdge); - HAL_Timer32_Channel_OCR_Set(timerChannel, timerChannel->OCR); - HAL_Timer32_Channel_ICR_Clear(timerChannel); - HAL_Timer32_Channel_Noise_Set(timerChannel, timerChannel->Noise); - - return HAL_OK; -} /* It is recommended to enable the timer in the following order: @@ -120,15 +137,15 @@ void analogWrite(uint32_t PinNumber, uint32_t writeVal) htimer32_channel.PWM_Invert = TIMER32_CHANNEL_NON_INVERTED_PWM; htimer32_channel.Mode = TIMER32_CHANNEL_MODE_PWM; htimer32_channel.CaptureEdge = TIMER32_CHANNEL_CAPTUREEDGE_RISING; - // cast to uint64_t to avoid overflow when multiplying htimer32_channel.OCR = (uint32_t) (((uint64_t)pwmTopVal * writeVal) / WriteValMax); htimer32_channel.Noise = TIMER32_CHANNEL_FILTER_OFF; - Timer32_Channel_Init(&htimer32_channel); + HAL_Timer32_Channel_Init(&htimer32_channel); + // start timer with initialized channel HAL_Timer32_Channel_Enable(&htimer32_channel); HAL_Timer32_Value_Clear(&htimer32); HAL_Timer32_Start(&htimer32); - pwmIsInited = true; // if at least one channel is working, say that the module is initialized + pwmIsInited++; // increase inited channels qty } else if(PinNumber == 10) // pin d10 has pwm, but you cannot use it while spi is running ErrorMsgHandler("analogWrite(): D10 cannot be used as PWM pin while SPI is running"); @@ -165,21 +182,26 @@ It is recommended to turn off the timer in the following order: */ void analogWriteStop(uint32_t PinNumber) { - if (pwmIsInited) + if ((pwmIsInited > 0) && (digitalPinPwmIsOn(PinNumber))) { // load the timer address and channel number corresponding to the specified pin htimer32.Instance = pwmPinToTimer(PinNumber); htimer32_channel.TimerInstance = htimer32.Instance; htimer32_channel.ChannelIndex = pwmPinToTimerChannel(PinNumber); - // in the initChannel function they do it inside, but in deinit they don't. We do it outside - htimer32_channel.Instance = (TIMER32_CHANNEL_TypeDef *)&(htimer32_channel.TimerInstance->CHANNELS[htimer32_channel.ChannelIndex]); - // и все чистим/отключаем - HAL_Timer32_InterruptMask_Clear(&htimer32, 0xFFFFFFFF); - HAL_Timer32_Prescaler_Set(&htimer32, 0); - HAL_Timer32_InterruptFlags_ClearMask(&htimer32, 0xFFFFFFFF); + // deinit channel HAL_Timer32_Channel_DeInit(&htimer32_channel); - HAL_Timer32_Stop(&htimer32); - pwmIsInited = false; + pwmIsInited--; // decrease inited channels qty + + // stop timer if no inited channels left + if (pwmIsInited == 0) + { + HAL_Timer32_Stop(&htimer32); + HAL_Timer32_Deinit(&htimer32); + } + + // config pin as input when timer channel off + HAL_GPIO_PinConfig(digitalPinToPort(PinNumber), digitalPinToBitMask(PinNumber), + HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); } } diff --git a/cores/arduino/wiring_analog.h b/cores/arduino/wiring_analog.h index 2e90d8d..5c3a4b4 100644 --- a/cores/arduino/wiring_analog.h +++ b/cores/arduino/wiring_analog.h @@ -16,6 +16,13 @@ extern "C" { */ uint32_t analogRead(uint32_t PinNumber); +/* + * \brief Set the resolution of adc results. Default is 10 bits (range from 0 to 1023). + * + * \param resolution 1...32 + */ +void analogReadResolution(uint8_t resolution); + /* * \brief Writes an analog value (PWM wave) to a pin. * diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index 9f82c27..1894c4f 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -24,8 +24,6 @@ extern "C" { #endif -extern void ErrorMsgHandler(const char * msg); - // initialization void pinMode(uint32_t PinNumber, uint32_t PinMode) { diff --git a/cores/arduino/wiring_time.c b/cores/arduino/wiring_time.c index 19dde62..ed23435 100644 --- a/cores/arduino/wiring_time.c +++ b/cores/arduino/wiring_time.c @@ -39,6 +39,11 @@ void SysTick_Init(void) HAL_SCR1_Timer_Init(&hscr1_timer); // Ticks are 32 MHz } +uint64_t SysTick_GetTicks(void) +{ + return TICKS_IN_SYSTIMER; +} + // number of microseconds since start of the program uint32_t micros(void) { diff --git a/cores/arduino/wiring_time.h b/cores/arduino/wiring_time.h index 162fbc3..9f1e690 100644 --- a/cores/arduino/wiring_time.h +++ b/cores/arduino/wiring_time.h @@ -24,12 +24,22 @@ extern "C" { #endif +#define clockCyclesPerMicrosecond() (F_CPU / 1000000L) +#define clockCyclesToMicroseconds(a) ((a) / clockCyclesPerMicrosecond()) +#define microsecondsToClockCycles(a) ((a) * clockCyclesPerMicrosecond()) + /** * \brief Initialize timer for creating delays * */ void SysTick_Init(void); +/** + * \brief Returns the number of ticks since the Arduino board began running the current program. + + * \return Number of ticks since the program started + */ +uint64_t SysTick_GetTicks(void); /** * \brief Returns the number of milliseconds since the Arduino board began running the current program. diff --git a/docs/pinout.PNG b/docs/pinout.PNG new file mode 100644 index 0000000..356af6c Binary files /dev/null and b/docs/pinout.PNG differ diff --git a/libraries/EEPROM/.gitignore b/libraries/EEPROM/.gitignore new file mode 100644 index 0000000..5bce97a --- /dev/null +++ b/libraries/EEPROM/.gitignore @@ -0,0 +1,2 @@ +installed.json +.vscode/ \ No newline at end of file diff --git a/libraries/EEPROM/README.md b/libraries/EEPROM/README.md new file mode 100644 index 0000000..9ca761d --- /dev/null +++ b/libraries/EEPROM/README.md @@ -0,0 +1,139 @@ +## **EEPROM Library V2.0** for Arduino + +**Written by:** _Christopher Andrews_. + +### **What is the EEPROM library.** + +Th EEPROM library provides an easy to use interface to interact with the internal non-volatile storage found in AVR based Arduino boards. This library will work on many AVR devices like ATtiny and ATmega chips. + +### **How to use it** +The EEPROM library is included in your IDE download. To add its functionality to your sketch you'll need to reference the library header file. You do this by adding an include directive to the top of your sketch. + +```Arduino +#include + +void setup(){ + +} + +void loop(){ + +} + +``` + +The library provides a global variable named `EEPROM`, you use this variable to access the library functions. The methods provided in the EEPROM class are listed below. + +You can view all the examples [here](examples/). + +### **Library functions** + +#### **`EEPROM.read( address )`** [[_example_]](examples/eeprom_read/eeprom_read.ino) + +This function allows you to read a single byte of data from the eeprom. +Its only parameter is an `int` which should be set to the address you wish to read. + +The function returns an `unsigned char` containing the value read. + +#### **`EEPROM.write( address, value )`** [[_example_]](examples/eeprom_write/eeprom_write.ino) + +The `write()` method allows you to write a single byte of data to the EEPROM. +Two parameters are needed. The first is an `int` containing the address that is to be written, and the second is a the data to be written (`unsigned char`). + +This function does not return any value. + +#### **`EEPROM.update( address, value )`** [[_example_]](examples/eeprom_update/eeprom_update.ino) + +This function is similar to `EEPROM.write()` however this method will only write data if the cell contents pointed to by `address` is different to `value`. This method can help prevent unnecessary wear on the EEPROM cells. + +This function does not return any value. + +#### **`EEPROM.get( address, object )`** [[_example_]](examples/eeprom_get/eeprom_get.ino) + +This function will retrieve any object from the EEPROM. +Two parameters are needed to call this function. The first is an `int` containing the address that is to be written, and the second is the object you would like to read. + +This function returns a reference to the `object` passed in. It does not need to be used and is only returned for convenience. + +#### **`EEPROM.put( address, object )`** [[_example_]](examples/eeprom_put/eeprom_put.ino) + +This function will write any object to the EEPROM. +Two parameters are needed to call this function. The first is an `int` containing the address that is to be written, and the second is the object you would like to write. + +This function uses the _update_ method to write its data, and therefore only rewrites changed cells. + +This function returns a reference to the `object` passed in. It does not need to be used and is only returned for convenience. + +#### **Subscript operator: `EEPROM[address]`** [[_example_]](examples/eeprom_crc/eeprom_crc.ino) + +This operator allows using the identifier `EEPROM` like an array. +EEPROM cells can be read _and_ **_written_** directly using this method. + +This operator returns a reference to the EEPROM cell. + +```c++ +unsigned char val; + +//Read first EEPROM cell. +val = EEPROM[ 0 ]; + +//Write first EEPROM cell. +EEPROM[ 0 ] = val; + +//Compare contents +if( val == EEPROM[ 0 ] ){ + //Do something... +} +``` + +#### **`EEPROM.length()`** + +This function returns an `unsigned int` containing the number of cells in the EEPROM. + +--- + +### **Advanced features** + +This library uses a component based approach to provide its functionality. This means you can also use these components to design a customized approach. Two background classes are available for use: `EERef` & `EEPtr`. + +#### **`EERef` class** + +This object references an EEPROM cell. +Its purpose is to mimic a typical byte of RAM, however its storage is the EEPROM. +This class has an overhead of two bytes, similar to storing a pointer to an EEPROM cell. + +```C++ +EERef ref = EEPROM[ 10 ]; //Create a reference to 11th cell. + +ref = 4; //write to EEPROM cell. + +unsigned char val = ref; //Read referenced cell. +``` + +#### **`EEPtr` class** + +This object is a bidirectional pointer to EEPROM cells represented by `EERef` objects. +Just like a normal pointer type, this type can be dereferenced and repositioned using +increment/decrement operators. + +```C++ +EEPtr ptr = 10; //Create a pointer to 11th cell. + +*ptr = 4; //dereference and write to EEPROM cell. + +unsigned char val = *ptr; //dereference and read. + +ptr++; //Move to next EEPROM cell. +``` + +#### **`EEPROM.begin()`** + +This function returns an `EEPtr` pointing to the first cell in the EEPROM. +This is useful for STL objects, custom iteration and C++11 style ranged for loops. + +#### **`EEPROM.end()`** + +This function returns an `EEPtr` pointing at the location after the last EEPROM cell. +Used with `begin()` to provide custom iteration. + +**Note:** The `EEPtr` returned is invalid as it is out of range. In fact the hardware causes wrapping of the address (overflow) and `EEPROM.end()` actually references the first EEPROM cell. diff --git a/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino b/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino new file mode 100644 index 0000000..834d14a --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_clear/eeprom_clear.ino @@ -0,0 +1,32 @@ +/* + * EEPROM Clear + * + * Sets all of the bytes of the EEPROM to 0. + * Please see eeprom_iteration for a more in depth + * look at how to traverse the EEPROM. + * + * This example code is in the public domain. + */ + +#include + +void setup() { + // initialize the LED pin as an output. + pinMode(LED_BUILTIN, OUTPUT); + EEPROM.begin(); + + /*** + Iterate through each byte of the EEPROM storage. + ***/ + + for (int i = 0 ; i < EEPROM.length() ; i++) { + EEPROM.write(i, 0); + } + + // turn the LED on when we're done + digitalWrite(LED_BUILTIN, HIGH); +} + +void loop() { + /** Empty loop. **/ +} diff --git a/libraries/EEPROM/examples/eeprom_crc/eeprom_crc.ino b/libraries/EEPROM/examples/eeprom_crc/eeprom_crc.ino new file mode 100644 index 0000000..504e1d6 --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_crc/eeprom_crc.ino @@ -0,0 +1,51 @@ +/*** + CRC algorithm generated by pycrc ( https://github.com/tpircher/pycrc ). + + A CRC is a simple way of checking whether data has changed or become corrupted. + This example calculates a CRC value directly on the EEPROM values. + The purpose of this example is to highlight how the EEPROM object can be used just like an array. +***/ + +#include + +void setup() { + + //Start serial + Serial.begin(9600); + EEPROM.begin(); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + //Print length of data to run CRC on. + Serial.print("EEPROM length: "); + Serial.println(EEPROM.length()); + + //Print the result of calling eeprom_crc() + Serial.print("CRC32 of EEPROM data: 0x"); + Serial.println(eeprom_crc(), HEX); + Serial.print("\n\nDone!"); +} + +void loop() { + /* Empty loop */ +} + +unsigned long eeprom_crc(void) { + + const unsigned long crc_table[16] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + + unsigned long crc = ~0L; + + for (int index = 0 ; index < EEPROM.length() ; ++index) { + crc = crc_table[(crc ^ EEPROM[index]) & 0x0f] ^ (crc >> 4); + crc = crc_table[(crc ^ (EEPROM[index] >> 4)) & 0x0f] ^ (crc >> 4); + crc = ~crc; + } + return crc; +} diff --git a/libraries/EEPROM/examples/eeprom_get/eeprom_get.ino b/libraries/EEPROM/examples/eeprom_get/eeprom_get.ino new file mode 100644 index 0000000..c49a31a --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_get/eeprom_get.ino @@ -0,0 +1,66 @@ +/*** + eeprom_get example. + + This shows how to use the EEPROM.get() method. + + To pre-set the EEPROM data, run the example sketch eeprom_put. + This sketch will run without it, however, the values shown + will be shown from what ever is already on the EEPROM. + + This may cause the serial object to print out a large string + of garbage if there is no null character inside one of the strings + loaded. +***/ + +#include + +void setup() { + EEPROM.begin(); + + float f = 0.00f; //Variable to store data read from EEPROM. + int eeAddress = 0; //EEPROM address to start reading from + + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + Serial.print("Read float from EEPROM: "); + + //Get the float data from the EEPROM at position 'eeAddress' + EEPROM.get(eeAddress, f); + Serial.println(f, 3); //This may print 'ovf, nan' if the data inside the EEPROM is not a valid float. + + /*** + As get also returns a reference to 'f', you can use it inline. + E.g: Serial.print( EEPROM.get( eeAddress, f ) ); + ***/ + + /*** + Get can be used with custom structures too. + I have separated this into an extra function. + ***/ + + secondTest(); //Run the next test. +} + +struct MyObject { + float field1; + byte field2; + char name[10]; +}; + +void secondTest() { + int eeAddress = sizeof(float); //Move address to the next byte after float 'f'. + + MyObject customVar; //Variable to store custom object read from EEPROM. + EEPROM.get(eeAddress, customVar); + + Serial.println("Read custom object from EEPROM: "); + Serial.println(customVar.field1); + Serial.println(customVar.field2); + Serial.println(customVar.name); +} + +void loop() { + /* Empty loop */ +} diff --git a/libraries/EEPROM/examples/eeprom_iteration/eeprom_iteration.ino b/libraries/EEPROM/examples/eeprom_iteration/eeprom_iteration.ino new file mode 100644 index 0000000..c01d241 --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_iteration/eeprom_iteration.ino @@ -0,0 +1,55 @@ +/*** + eeprom_iteration example. + + A set of example snippets highlighting the + simplest methods for traversing the EEPROM. + + Running this sketch is not necessary, this is + simply highlighting certain programming methods. +***/ + +#include + +void setup() { + EEPROM.begin(); + + /*** + Iterate the EEPROM using a for loop. + ***/ + + for (int index = 0 ; index < EEPROM.length() ; index++) { + + //Add one to each cell in the EEPROM + EEPROM[ index ] += 1; + } + + /*** + Iterate the EEPROM using a while loop. + ***/ + + int index = 0; + + while (index < EEPROM.length()) { + + //Add one to each cell in the EEPROM + EEPROM[ index ] += 1; + index++; + } + + /*** + Iterate the EEPROM using a do-while loop. + ***/ + + int idx = 0; //Used 'idx' to avoid name conflict with 'index' above. + + do { + + //Add one to each cell in the EEPROM + EEPROM[ idx ] += 1; + idx++; + } while (idx < EEPROM.length()); + + +} //End of setup function. + +void loop() {} diff --git a/libraries/EEPROM/examples/eeprom_put/eeprom_put.ino b/libraries/EEPROM/examples/eeprom_put/eeprom_put.ino new file mode 100644 index 0000000..9c452f9 --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_put/eeprom_put.ino @@ -0,0 +1,56 @@ +/*** + eeprom_put example. + + This shows how to use the EEPROM.put() method. + Also, this sketch will pre-set the EEPROM data for the + example sketch eeprom_get. + + Note, unlike the single byte version EEPROM.write(), + the put method will use update semantics. As in a byte + will only be written to the EEPROM if the data is actually + different. +***/ + +#include + +struct MyObject { + float field1; + byte field2; + char name[10]; +}; + +void setup() { + EEPROM.begin(); + + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + float f = 123.456f; //Variable to store in EEPROM. + int eeAddress = 0; //Location we want the data to be put. + + + //One simple call, with the address first and the object second. + EEPROM.put(eeAddress, f); + + Serial.println("Written float data type!"); + + /** Put is designed for use with custom structures also. **/ + + //Data to store. + MyObject customVar = { + 3.14f, + 65, + "Working!" + }; + + eeAddress += sizeof(float); //Move address to the next byte after float 'f'. + + EEPROM.put(eeAddress, customVar); + Serial.print("Written custom data type! \n\nView the example sketch eeprom_get to see how you can retrieve the values!"); +} + +void loop() { + /* Empty loop */ +} diff --git a/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino b/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino new file mode 100644 index 0000000..ec6e0cf --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_read/eeprom_read.ino @@ -0,0 +1,38 @@ +/* + * EEPROM Read + * + * Reads the value of each byte of the EEPROM and prints it + * to the computer. + */ + +#include + +// start reading from the first byte (address 0) of the EEPROM +int address = 0; +byte value; + +void setup() { + EEPROM.begin(); + // initialize serial and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } +} + +void loop() { + // read a byte from the current address of the EEPROM + value = EEPROM.read(address); + + Serial.print(address); + Serial.print("\t"); + Serial.print(value, DEC); + Serial.println(); + + address = address + 1; + if (address == EEPROM.length()) { + address = 0; + } + + delay(500); +} diff --git a/libraries/EEPROM/examples/eeprom_update/eeprom_update.ino b/libraries/EEPROM/examples/eeprom_update/eeprom_update.ino new file mode 100644 index 0000000..8ca0fe9 --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_update/eeprom_update.ino @@ -0,0 +1,50 @@ +/*** + EEPROM Update method + + Stores values read from analog input 0 into the EEPROM. + These values will stay in the EEPROM when the board is + turned off and may be retrieved later by another sketch. + + If a value has not changed in the EEPROM, it is not overwritten + which would reduce the life span of the EEPROM unnecessarily. + ***/ + +#include + +/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/ +int address = 0; + +void setup() { + EEPROM.begin(); +} + +void loop() { + /*** + need to divide by 4 because analog inputs range from + 0 to 1023 and each byte of the EEPROM can only hold a + value from 0 to 255. + ***/ + int val = analogRead(0) / 4; + + /*** + Update the particular EEPROM cell. + these values will remain there when the board is + turned off. + ***/ + EEPROM.update(address, val); + + /*** + The function EEPROM.update(address, val) is equivalent to the following: + + if( EEPROM.read(address) != val ){ + EEPROM.write(address, val); + } + ***/ + + address = address + 1; + if (address == EEPROM.length()) { + address = 0; + } + + delay(100); +} diff --git a/libraries/EEPROM/examples/eeprom_write/eeprom_write.ino b/libraries/EEPROM/examples/eeprom_write/eeprom_write.ino new file mode 100644 index 0000000..668fb64 --- /dev/null +++ b/libraries/EEPROM/examples/eeprom_write/eeprom_write.ino @@ -0,0 +1,41 @@ +/* + * EEPROM Write + * + * Stores values read from analog input 0 into the EEPROM. + * These values will stay in the EEPROM when the board is + * turned off and may be retrieved later by another sketch. + */ + +#include + +/** the current address in the EEPROM (i.e. which byte we're going to write to next) **/ +int addr = 0; + +void setup() { + EEPROM.begin(); +} + +void loop() { + /*** + Need to divide by 4 because analog inputs range from + 0 to 1023 and each byte of the EEPROM can only hold a + value from 0 to 255. + ***/ + + int val = analogRead(0) / 4; + + /*** + Write the value to the appropriate byte of the EEPROM. + these values will remain there when the board is + turned off. + ***/ + + EEPROM.write(addr, val); + + addr = addr + 1; + if (addr == EEPROM.length()) { + addr = 0; + } + + delay(100); +} diff --git a/libraries/EEPROM/keywords.txt b/libraries/EEPROM/keywords.txt new file mode 100644 index 0000000..2cabc0b --- /dev/null +++ b/libraries/EEPROM/keywords.txt @@ -0,0 +1,22 @@ +####################################### +# Syntax Coloring Map For EEPROM +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EEPROM KEYWORD1 +EERef KEYWORD1 +EEPtr KEYWORD2 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +update KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/libraries/EEPROM/library.properties b/libraries/EEPROM/library.properties new file mode 100644 index 0000000..d04fe51 --- /dev/null +++ b/libraries/EEPROM/library.properties @@ -0,0 +1,10 @@ +name=EEPROM +version=0.0.0 +author=Arduino, Christopher Andrews +maintainer=Elron +sentence=Enables reading and writing to the permanent board storage. +paragraph=This library allows to read and write data in a memory type, the EEPROM, that keeps its content also when the board is powered off. The amount of EEPROM available depends on the microcontroller type. +category=Data Storage +url=http://www.arduino.cc/en/Reference/EEPROM +architectures=MIK32_Amur + diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp new file mode 100644 index 0000000..10f5a89 --- /dev/null +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -0,0 +1,119 @@ +#include "EEPROM.h" +#include "mik32_hal_eeprom.h" +#include "mik32_hal.h" + +HAL_EEPROM_HandleTypeDef heeprom; + +void EEPROMClass:: begin() +{ + heeprom.Instance = EEPROM_REGS; + heeprom.Mode = HAL_EEPROM_MODE_TWO_STAGE; + heeprom.ErrorCorrection = HAL_EEPROM_ECC_ENABLE; + heeprom.EnableInterrupt = HAL_EEPROM_SERR_DISABLE; + + HAL_EEPROM_Init(&heeprom); + HAL_EEPROM_CalculateTimings(&heeprom, OSC_SYSTEM_VALUE); +} + +uint8_t read_byte( int idx ) +{ + // check if idx is valid + if (idx < 0) + { + idx = -idx; + ErrorMsgHandler("EEPROM.read(): The eeprom cell address must be non-negative"); + } + if ((uint32_t)idx >= (uint32_t)EEPROM_LENGHT) + { + idx = (int)((uint32_t)idx % EEPROM_LENGHT); + ErrorMsgHandler("EEPROM.read(): The address of the eeprom cell goes beyond the eeprom"); + } + + uint32_t read_data_buf[EEPROM_PAGE_WORDS] = {}; + + // calc start address of the desired page + uint32_t addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_PAGE_SIZE) * EEPROM_PAGE_SIZE; + // read desired page + HAL_EEPROM_Read(&heeprom, (uint16_t)addr, read_data_buf, EEPROM_PAGE_WORDS, EEPROM_OP_TIMEOUT); + // address of the searched word in eeprom: EEPROM_START_ADDR + (uint32_t)idx + uint32_t word_addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_WORD_SIZE) * EEPROM_WORD_SIZE; + // searched word index in the read_data_buf array: the word address in the eeprom minus the page beginning address + uint32_t word_idx = (word_addr - addr) / EEPROM_WORD_SIZE; + // byte number in a word + uint32_t byte_idx = ((uint32_t)idx) % EEPROM_WORD_SIZE; + // get desired byte from page data + return (uint8_t)(( read_data_buf[word_idx] & (((uint32_t)0xFF) << ((EEPROM_WORD_SIZE - byte_idx - 1) * 8)) ) >> ((EEPROM_WORD_SIZE - byte_idx - 1) * 8)); +} + +void write_byte( int idx, uint8_t val ) +{ + // check if idx is valid + if (idx < 0) + { + idx = -idx; + ErrorMsgHandler("EEPROM.write(): The eeprom cell address must be non-negative"); + } + if ((uint32_t)idx >= (uint32_t)EEPROM_LENGHT) + { + idx = (int)((uint32_t)idx % EEPROM_LENGHT); + ErrorMsgHandler("EEPROM.write(): The address of the eeprom cell goes beyond the eeprom"); + } + update_byte(idx, val); +} + +void update_byte( int idx, uint8_t val ) +{ + // check if idx is valid + if (idx < 0) + { + idx = -idx; + ErrorMsgHandler("EEPROM.update(): The eeprom cell address must be non-negative"); + } + if ((uint32_t)idx >= (uint32_t)EEPROM_LENGHT) + { + idx = (int)((uint32_t)idx % EEPROM_LENGHT); + ErrorMsgHandler("EEPROM.update(): The address of the eeprom cell goes beyond the eeprom"); + } + + uint32_t write_data_buf[EEPROM_PAGE_WORDS] = {}; + + // calc start address of the desired page + uint32_t addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_PAGE_SIZE) * EEPROM_PAGE_SIZE; + // read desired page + HAL_EEPROM_Read(&heeprom, (uint16_t)addr, write_data_buf, EEPROM_PAGE_WORDS, EEPROM_OP_TIMEOUT); + // address of the searched word in eeprom: EEPROM_START_ADDR + (uint32_t)idx + uint32_t word_addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_WORD_SIZE) * EEPROM_WORD_SIZE; + // searched word index in the write_data_buf array: the word address in the eeprom minus the page beginning address + uint32_t word_idx = (word_addr - addr) / EEPROM_WORD_SIZE; + // byte number in a word + uint32_t byte_idx = ((uint32_t)idx) % EEPROM_WORD_SIZE; + // get desired byte + uint32_t byte = ((uint32_t)val) << ((EEPROM_WORD_SIZE - byte_idx - 1) * 8); + uint8_t oldVal = (uint8_t)(*((uint8_t*)write_data_buf + word_idx * EEPROM_WORD_SIZE + (EEPROM_WORD_SIZE - byte_idx - 1))); + // checking if written byte is different from the new one + if(oldVal != val) + { + // clear page + HAL_EEPROM_Erase(&heeprom, (uint16_t)addr, EEPROM_PAGE_WORDS, HAL_EEPROM_WRITE_SINGLE, EEPROM_OP_TIMEOUT); + // get and replace the desired byte + write_data_buf[word_idx] = (write_data_buf[word_idx] & (~((uint32_t)(0xFF) << ((EEPROM_WORD_SIZE - byte_idx - 1) * 8)))) | byte; + + HAL_EEPROM_Write(&heeprom, (uint16_t)addr, write_data_buf, EEPROM_PAGE_WORDS, HAL_EEPROM_WRITE_SINGLE, EEPROM_OP_TIMEOUT); + } +} + + +void HAL_read(uint16_t addr, uint32_t * data) +{ + HAL_EEPROM_Read(&heeprom, (uint16_t)addr, data, EEPROM_PAGE_WORDS, EEPROM_OP_TIMEOUT); +} + +void HAL_write(uint16_t addr, uint32_t * data) +{ + HAL_EEPROM_Write(&heeprom, (uint16_t)addr, data, EEPROM_PAGE_WORDS, HAL_EEPROM_WRITE_SINGLE, EEPROM_OP_TIMEOUT); +} + +void HAL_erase(uint16_t addr) +{ + HAL_EEPROM_Erase(&heeprom, (uint16_t)addr, EEPROM_PAGE_WORDS, HAL_EEPROM_WRITE_SINGLE, EEPROM_OP_TIMEOUT); +} \ No newline at end of file diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h new file mode 100644 index 0000000..315c4e0 --- /dev/null +++ b/libraries/EEPROM/src/EEPROM.h @@ -0,0 +1,250 @@ +#ifndef EEPROM_h +#define EEPROM_h + +#include +#include +#include "string.h" + +#define EEPROM_OP_TIMEOUT 100000 +#define EEPROM_PAGE_WORDS 32 // words number per page +#define EEPROM_PAGE_COUNT 8 // user EEPROM pages number +#define EEPROM_START_ADDR 0x1C00 // user EEPROM start address +#define EEPROM_WORD_SIZE 4 // word takes 4 bytes +#define EEPROM_PAGE_SIZE ( EEPROM_PAGE_WORDS * EEPROM_WORD_SIZE ) // page takes 32*4 = 128 bytes +#define EEPROM_END 0x1FFF +#define EEPROM_LENGHT (EEPROM_PAGE_SIZE * EEPROM_PAGE_COUNT) + +void HAL_read(uint16_t addr, uint32_t * data); +void HAL_write(uint16_t addr, uint32_t * data); +void HAL_erase(uint16_t addr); +uint8_t read_byte( int idx ); +void write_byte( int idx, uint8_t val ); +void update_byte( int idx, uint8_t val ); + +struct EERef{ + + EERef( const int index ) + : index( index ) {} + + uint8_t operator*() const { return read_byte( index ); } + operator uint8_t() const { return **this; } + + EERef &operator=( const EERef &ref ) { return *this = *ref; } + EERef &operator=( uint8_t in ) { return write_byte( index, in ), *this; } + EERef &operator +=( uint8_t in ) { return *this = **this + in; } + EERef &operator -=( uint8_t in ) { return *this = **this - in; } + EERef &operator *=( uint8_t in ) { return *this = **this * in; } + EERef &operator /=( uint8_t in ) { return *this = **this / in; } + EERef &operator ^=( uint8_t in ) { return *this = **this ^ in; } + EERef &operator %=( uint8_t in ) { return *this = **this % in; } + EERef &operator &=( uint8_t in ) { return *this = **this & in; } + EERef &operator |=( uint8_t in ) { return *this = **this | in; } + EERef &operator <<=( uint8_t in ) { return *this = **this << in; } + EERef &operator >>=( uint8_t in ) { return *this = **this >> in; } + + EERef &update( uint8_t in ) { return in != *this ? *this = in : *this; } + + /** Prefix increment/decrement **/ + EERef& operator++() { return *this += 1; } + EERef& operator--() { return *this -= 1; } + + /** Postfix increment/decrement **/ + uint8_t operator++ (int){ + uint8_t ret = **this; + return ++(*this), ret; + } + + uint8_t operator-- (int){ + uint8_t ret = **this; + return --(*this), ret; + } + + int index; +}; + +struct EEPtr{ + + EEPtr( const int index ) + : index( index ) {} + + operator int() const { return index; } + EEPtr &operator=( int in ) { return index = in, *this; } + + bool operator!=( const EEPtr &ptr ) { return index != ptr.index; } + EERef operator*() { return index; } + + EEPtr& operator++() { return ++index, *this; } + EEPtr& operator--() { return --index, *this; } + EEPtr operator++ (int) { return index++; } + EEPtr operator-- (int) { return index--; } + + int index; +}; + +struct EEPROMClass{ + + //Basic user access methods. + EERef operator[]( const int idx ) { return idx; } + uint8_t read( int idx ) { return EERef( idx ); } + void write( int idx, uint8_t val ) { (EERef( idx )) = val; } + void update( int idx, uint8_t val ) { update_byte(idx, val); } + + void begin(); + EEPtr end() { return length(); } // Standards requires this to be the item after the last valid entry. The returned pointer is invalid. + uint16_t length() { return (uint16_t)EEPROM_LENGHT; } + + template< typename T > + T &put(int idx, T &data) + { + void* dataPointer = (void*)&data; + // check if idx is valid + if (idx < 0) + { + idx = -idx; + ErrorMsgHandler("EEPROM.put(): The eeprom cell address must be non-negative"); + } + if ((uint32_t)idx >= (uint32_t)EEPROM_LENGHT) + { + idx = (int)((uint32_t)idx % EEPROM_LENGHT); + ErrorMsgHandler("EEPROM.put(): The address of the eeprom cell goes beyond the eeprom"); + } + + uint32_t write_data_buf[EEPROM_PAGE_WORDS] = {}; + uint32_t dataSize = sizeof(data); // writing data size + uint32_t dataShift = 0; // shift of the data writing start address + uint32_t writeSize = dataSize; + + // calc start address of the desired page + uint32_t addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_PAGE_SIZE) * EEPROM_PAGE_SIZE; + // address of the searched word in eeprom: EEPROM_START_ADDR + (uint32_t)idx + uint32_t word_addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_WORD_SIZE) * EEPROM_WORD_SIZE; + // searched word index in the write_data_buf array: the word address in the eeprom minus the page beginning address + uint32_t word_idx = (word_addr - addr) / EEPROM_WORD_SIZE; + // page data start address + uint32_t byte_addr = (uint32_t)idx % EEPROM_PAGE_SIZE; + // byte number in a word + uint32_t byte_idx = ((uint32_t)idx) % EEPROM_WORD_SIZE; + // read first page + HAL_read((uint16_t)addr, write_data_buf); + // if data does not fit on the first page, then write down only what fits + if (EEPROM_PAGE_SIZE - byte_addr < dataSize) + writeSize = EEPROM_PAGE_SIZE - byte_addr; + uint32_t lastWord = (writeSize + byte_idx - 1) / EEPROM_WORD_SIZE + word_idx; + dataSize -= writeSize; + // write data page by page, first separately write the first page + memcpy((void *)((uint8_t *)write_data_buf + byte_addr), (void*)dataPointer, writeSize); + // prepare words for writing + for(uint8_t i = word_idx; i <= lastWord; i++) + { + uint32_t word = write_data_buf[i]; + write_data_buf[i] = 0; + write_data_buf[i] = ((word & 0xFF)<<24) | ((word & (0xFF<<8))<<8) | ((word & (0xFF<<16))>>8) | ((word & (0xFF<<24))>>24); + } + HAL_erase((uint16_t)addr); + HAL_write((uint16_t)addr, write_data_buf); + // if there is data left after writing the first page, then write it page by page until it runs out + while (dataSize > 0) + { + addr += EEPROM_PAGE_SIZE; + // if reaching the eeprom end address, return to the initial address + if (addr == EEPROM_START_ADDR + EEPROM_LENGHT) + addr = EEPROM_START_ADDR; + HAL_read((uint16_t)addr, write_data_buf); + dataShift += writeSize; + writeSize = dataSize; + if (EEPROM_PAGE_SIZE < dataSize) + writeSize = EEPROM_PAGE_SIZE; + lastWord = (writeSize - 1) / EEPROM_WORD_SIZE; + memcpy((void *)(write_data_buf), (void*)((uint8_t *)dataPointer + dataShift), writeSize); + // prepare words for writing + for(uint8_t i = 0; i <= lastWord; i++) + { + uint32_t word = write_data_buf[i]; + write_data_buf[i] = 0; + write_data_buf[i] = ((word & 0xFF)<<24) | ((word & (0xFF<<8))<<8) | ((word & (0xFF<<16))>>8) | ((word & (0xFF<<24))>>24); + } + HAL_erase((uint16_t)addr); + HAL_write((uint16_t)addr, write_data_buf); + dataSize -= writeSize; + } + return data; + } + + template< typename T > + T &get(int idx, T &data) + { + void* dataPointer = (void*)&data; + // check if idx is valid + if (idx < 0) + { + idx = -idx; + ErrorMsgHandler("EEPROM.get(): The eeprom cell address must be non-negative"); + } + if ((uint32_t)idx >= (uint32_t)EEPROM_LENGHT) + { + idx = (int)((uint32_t)idx % EEPROM_LENGHT); + ErrorMsgHandler("EEPROM.get(): The address of the eeprom cell goes beyond the eeprom"); + } + + uint32_t read_data_buf[EEPROM_PAGE_WORDS] = {}; + uint32_t dataSize = sizeof(data); // reading data size + uint32_t dataShift = 0; // shift of the data reading start address + uint32_t readSize = dataSize; + + // calc start address of the desired page + uint32_t addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_PAGE_SIZE) * EEPROM_PAGE_SIZE; + // address of the searched word in eeprom: EEPROM_START_ADDR + (uint32_t)idx + uint32_t word_addr = EEPROM_START_ADDR + (((uint32_t)idx) / EEPROM_WORD_SIZE) * EEPROM_WORD_SIZE; + // searched word index in the read_data_buf array: the word address in the eeprom minus the page beginning address + uint32_t word_idx = (word_addr - addr) / EEPROM_WORD_SIZE; + // page data start address + uint32_t byte_addr = (uint32_t)idx % EEPROM_PAGE_SIZE; + // byte number in a word + uint32_t byte_idx = ((uint32_t)idx) % EEPROM_WORD_SIZE; + // read first page + HAL_read((uint16_t)addr, read_data_buf); + if (EEPROM_PAGE_SIZE - byte_addr < dataSize) + readSize = EEPROM_PAGE_SIZE - byte_addr; + uint32_t lastWord = (readSize + byte_idx - 1) / EEPROM_WORD_SIZE + word_idx; + dataSize -= readSize; + // prepare words + for(uint8_t i = word_idx; i <= lastWord; i++) + { + uint32_t word = read_data_buf[i]; + read_data_buf[i] = 0; + read_data_buf[i] = ((word & 0xFF)<<24) | ((word & (0xFF<<8))<<8) | ((word & (0xFF<<16))>>8) | ((word & (0xFF<<24))>>24); + } + // read data page by page, first separately read the first page + memcpy((void *)dataPointer, (void*)((uint8_t *)read_data_buf + byte_addr), readSize); + + // if there is data left after reading the first page, then read it page by page until it runs out + while (dataSize > 0) + { + addr += EEPROM_PAGE_SIZE; + // if reaching the eeprom end address, return to the initial address + if (addr == EEPROM_START_ADDR + EEPROM_LENGHT) + addr = EEPROM_START_ADDR; + HAL_read((uint16_t)addr, read_data_buf); + dataShift += readSize; + readSize = dataSize; + if (EEPROM_PAGE_SIZE < dataSize) + readSize = EEPROM_PAGE_SIZE; + lastWord = (dataSize - 1) / EEPROM_WORD_SIZE; + // prepare words + for(uint8_t i = 0; i <= lastWord; i++) + { + uint32_t word = read_data_buf[i]; + read_data_buf[i] = 0; + read_data_buf[i] = ((word & 0xFF)<<24) | ((word & (0xFF<<8))<<8) | ((word & (0xFF<<16))>>8) | ((word & (0xFF<<24))>>24); + } + memcpy((void *)((uint8_t *)dataPointer + dataShift), (void*)(read_data_buf), readSize); + dataSize -= readSize; + } + return data; // Return passed object pointer with the read data + } +}; + +#pragma GCC diagnostic ignored "-Wunused-variable" // for GCC and Clang + +static EEPROMClass EEPROM; +#endif diff --git a/libraries/MFRC522/README.rst b/libraries/MFRC522/README.rst new file mode 100644 index 0000000..30b96fa --- /dev/null +++ b/libraries/MFRC522/README.rst @@ -0,0 +1,392 @@ +MFRC522 +======= + +.. image:: https://img.shields.io/maintenance/no/2019.svg + :target: `development`_ +.. image:: https://github.com/miguelbalboa/rfid/workflows/PlatformIO%20CI/badge.svg + :target: https://github.com/miguelbalboa/rfid/actions + :alt: GitHub Actions +.. image:: https://img.shields.io/badge/C%2B%2B-11-brightgreen.svg + :target: `compatible ide`_ +.. image:: https://img.shields.io/github/release/miguelbalboa/rfid.svg?colorB=green + :target: https://github.com/miguelbalboa/rfid/releases + :alt: Releases +.. image:: https://img.shields.io/badge/ArduinoIDE-%3E%3D1.6.10-lightgrey.svg + :target: `compatible ide`_ + +Arduino library for MFRC522 and other RFID RC522 based modules. + +Read and write different types of Radio-Frequency IDentification (RFID) cards +on your Arduino using a RC522 based reader connected via the Serial Peripheral +Interface (SPI) interface. + +For advanced and further development please use library `RFID_MFRC522v2 `_. + +.. _development: +Development +----------- + +**The development by owner miguelbalboa has ended**. + +**Feature status: complete freeze**; no function or API change. + +**Code status: partial freeze**; just fixes/typos or documentation updates; *no* extensions for other boards; *no* new examples. + +**Maintenance status: sporadically**. + +**Why no further development?** +This library has a long history and is used in many projects. These projects often do not document what version they use. Committing changes might break those old projects and lead to bad experiences (for beginners) and support requests. For these reasons the library is in freeze mode. You can still commit typo, documentation or bug fixes. + + +.. _before buy: +Before buy +---------- +Please notice that there are many sellers (ebay, aliexpress, ..) who sell mfrc522 boards. **The quality of these boards are extremely different.** Some are soldered with wrong/low quality capacitors or fake/defect mfrc522. + +**Please consider buying several devices from different suppliers.** So the chance of getting a working device is higher. + +If you got a bad board and you can tell us how to detect those boards (silk, chip description, ..), please share your knowledge. + + +.. _what works and not: +What works and not? +------------------- + +* **Works** + + #. Communication (Crypto1) with MIFARE Classic (1k, 4k, Mini). + #. Communication (Crypto1) with MIFARE Classic compatible PICCs. + #. Firmware self check of MFRC522. + #. Set the UID, write to sector 0, and unbrick Chinese UID changeable MIFARE cards. + #. Manage the SPI chip select pin (aka SS, SDA) + +* **Works partially** + + #. Communication with MIFARE Ultralight. + #. Other PICCs (Ntag216). + #. More than 2 modules, require a multiplexer `#191 `_. + +* **Doesn't work** + + #. MIFARE DESFire, MIFARE DESFire EV1/EV2, not supported by software. + #. Communication with 3DES or AES, not supported by software. + #. Peer-to-peer (ISO/IEC 18092), not `supported by hardware`_. + #. Communication with smart phone, not `supported by hardware`_. + #. Card emulation, not `supported by hardware`_. + #. Use of IRQ pin. But there is a proof-of-concept example. + #. With Intel Galileo (Gen2) see `#310 `__, not supported by software. + #. Power reduction modes `#269 `_, not supported by software. + #. I2C instead of SPI `#240 `_, not supported by software. + #. UART instead of SPI `#281 `_, not supported by software. + +* **Need more?** + + #. If software: code it and make a pull request. + #. If hardware: buy a more expensive like PN532 (supports NFC and many more, but costs about $15 and not usable with this library). + + +.. _compatible ide: +Compatible IDE +-------------- +This library works with Arduino IDE 1.6, older versions are **not supported** and will cause compiler errors. The built-in library manager is supported. + +If you use your own compiler, you have to enable ``c++11``-support. + + +.. _compatible boards: +Compatible boards +----------------- + +**!!!Only for advanced users!!!** + +This library is compatible with the Teensy and ESP8266 if you use the board plugin of the Arduino IDE. Not all examples are available for every board. You also have to change pins. See `pin layout`_. + +Some user made some patches/suggestions/ports for other boards: + +* Linux: https://github.com/miguelbalboa/rfid/pull/216 +* chipKIT: https://github.com/miguelbalboa/rfid/pull/230 +* ESP8266 (native): https://github.com/miguelbalboa/rfid/pull/235 +* LPCOPen (in C): https://github.com/miguelbalboa/rfid/pull/258 + +Note that the main target/support of library is still Arduino. + +.. _support issue: +Support/issue +------------- +1. First checkout `what works and not`_ and `troubleshooting`_ . + +2. It seems to be a hardware issue or you need support to program your project? + Please ask in the official `Arduino forum`_, where you would get a much faster answer than on Github. + +3. It seems to be a software issue? + Open an issue on Github. + + +.. _code style: +Code style +---------- + +Please use ``fixed integers``, see `stdint.h`_. Why? This library is compatible with different boards which use different architectures (16bit and 32bit.) Unfixed ``int`` variables have different sizes in different environments and may cause unpredictable behaviour. + + +.. _pin layout: +Pin Layout +---------- + +The following table shows the typical pin layout used: + ++-----------+----------+-----------------------------------------------------------------------------------+ +| | PCD | Arduino | +| +----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| | MFRC522 | Uno / 101 | Mega | Nano v3 |Leonardo / Micro | Pro Micro | Yun [4]_| Due | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| Signal | Pin | Pin | Pin | Pin | Pin | Pin | Pin | Pin | ++===========+==========+=============+=========+=========+=================+===========+=========+=========+ +| RST/Reset | RST | 9 [1]_ | 5 [1]_ | D9 | RESET / ICSP-5 | RST | Pin9 | 22 [1]_ | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| SPI SS | SDA [3]_ | 10 [2]_ | 53 [2]_ | D10 | 10 | 10 | Pin10 | 23 [2]_ | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| SPI MOSI | MOSI | 11 / ICSP-4 | 51 | D11 | ICSP-4 | 16 | ICSP4 | SPI-4 | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| SPI MISO | MISO | 12 / ICSP-1 | 50 | D12 | ICSP-1 | 14 | ICSP1 | SPI-1 | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ +| SPI SCK | SCK | 13 / ICSP-3 | 52 | D13 | ICSP-3 | 15 | ICSP3 | SPI-3 | ++-----------+----------+-------------+---------+---------+-----------------+-----------+---------+---------+ + ++-----------+---------------+--------------------------+-------------+ +| | ESP8266 | Teensy | 8F328P-U | +| +---------------+--------+--------+--------+-------------+ +| | Wemos D1 mini | 2.0 | ++ 2.0 | 3.1 | ALPHA | ++-----------+---------------+--------+--------+--------+-------------+ +| Signal | Pin | Pin | Pin | Pin | Pin [5]_ | ++===========+===============+========+========+========+=============+ +| RST/Reset | D3 | 7 | 4 | 9 | D9# [1]_ | ++-----------+---------------+--------+--------+--------+-------------+ +| SPI SS | D8 | 0 | 20 | 10 | D10# [2]_ | ++-----------+---------------+--------+--------+--------+-------------+ +| SPI MOSI | D7 | 2 | 22 | 11 | MOSI / D11# | ++-----------+---------------+--------+--------+--------+-------------+ +| SPI MISO | D6 | 3 | 23 | 12 | MISO / D12# | ++-----------+---------------+--------+--------+--------+-------------+ +| SPI SCK | D5 | 1 | 21 | 13 | SCK | ++-----------+---------------+--------+--------+--------+-------------+ + +.. [1] Configurable, typically defined as RST_PIN in sketch/program. +.. [2] Configurable, typically defined as SS_PIN in sketch/program. +.. [3] The SDA pin might be labeled SS on some/older MFRC522 boards. +.. [4] Source: `#111 `_ . +.. [5] Pin names from the back (empty) side of the board were used as more definitive. + +Important: If your micro controller supports multiple SPI interfaces, the library only uses the **default (first) SPI** of the Arduino framework. + +.. _hardware: +Hardware +-------- + +There are three hardware components involved: + +1. **Micro Controller**: + +* An `Arduino`_ or compatible executing the Sketch using this library. + +* Prices vary from USD 7 for clones, to USD 75 for "starter kits" (which + might be a good choice if this is your first exposure to Arduino; + check if such kit already includes the Arduino, Reader, and some Tags). + +2. **Proximity Coupling Device (PCD)**: + +* The PCD is the actual RFID **Reader** based on the `NXP MFRC522`_ Contactless + Reader Integrated Circuit. + +* Readers can be found on `eBay`_ for around USD 5: search for *"rc522"*. + +* You can also find them on several web stores. They are often included in + *"starter kits"*, so check your favourite electronics provider as well. + +3. **Proximity Integrated Circuit Card (PICC)**: + +* The PICC is the RFID **Card** or **Tag** using the `ISO/IEC 14443A`_ + interface, for example Mifare or NTAG203. + +* One or two might be included with the Reader or *"starter kit"* already. + + +.. _protocol: +Protocols +--------- + +1. The micro controller and the reader use SPI for communication. + +* The protocol is described in the `NXP MFRC522`_ datasheet. + +* See the `Pin Layout`_ section for details on connecting the pins. + +2. The reader and the tags communicate using a 13.56 MHz electromagnetic field. + +* The protocol is defined in ISO/IEC 14443-3:2011 Part 3 Type A. + + * Details are found in chapter 6 *"Type A – Initialization and anticollision"*. + + * See http://wg8.de/wg8n1496_17n3613_Ballot_FCD14443-3.pdf for a free version + of the final draft (which might be outdated in some areas). + + * The reader does not support ISO/IEC 14443-3 Type B. + + +.. _security: +Security +-------- +* The **UID** of a card **can not be used** as an unique identification for security related projects. Some Chinese cards allow to change the UID which means you can easily clone a card. For projects like *access control*, *door opener* or *payment systems* you **must implement** an **additional security mechanism** like a password or normal key. + +* This library only supports crypto1-encrypted communication. Crypto1 has been known as `broken`_ for a few years, so it does NOT offer ANY security, it is virtually unencrypted communication. **Do not use it for any security related applications!** + +* This library does not offer 3DES or AES authentication used by cards like the Mifare DESFire, it may be possible to be implemented because the datasheet says there is support. We hope for pull requests :). + + +.. _troubleshooting: +Troubleshooting +--------------- + +* **I don't get input from reader** or **WARNING: Communication failure, is the MFRC522 properly connected?** + + #. Check your physical connection, see `Pin Layout`_ . + #. Check your pin settings/variables in the code, see `Pin Layout`_ . + #. Check your pin header soldering. Maybe you have cold solder joints. + #. Check your power supply. Maybe add a capacitor between 3.3V and GND to stabilize the power #560, sometimes an additional delay after `PCD_Init()` can help. + #. Check voltage. Most breakouts work with 3.3V. + #. SPI only works with 3.3V, most breakouts seem 5V tolerant, but try a level shifter. + #. SPI does not like long connections. Try shorter connections. + #. SPI does not like prototyping boards. Try soldered connections. + #. According to reports #101, #126 and #131, there may be a problem with the soldering on the MFRC522 breakout. You could fix this on your own. + + +* **Firmware Version: 0x12 = (unknown) or other random values** + + #. The exact reason of this behaviour is unknown. + #. Some boards need more time after `PCD_Init()` to be ready. As workaround add a `delay(4)` directly after `PCD_Init()` to give the PCD more time. + #. If this sometimes appears, a bad connection or power source is the reason. + #. If the firmware version is reported permanent, it is very likely that the hardware is a fake or has a defect. Contact your supplier. + + +* **Sometimes I get timeouts** or **sometimes tag/card does not work.** + + #. Try the other side of the antenna. + #. Try to decrease the distance between the MFRC522 and your tag. + #. Increase the antenna gain per firmware: ``mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);`` + #. Use better power supply. + #. Hardware may be corrupted, most products are from china and sometimes the quality is really poor. Contact your seller. + + +* **My tag/card doesn't work.** + + #. Distance between antenna and token too large (>1cm). + #. You got the wrong type PICC. Is it really 13.56 MHz? Is it really a Mifare Type A? + #. NFC tokens are not supported. Some may work. + #. Animal RFID tags are not supported. They use a different frequency (125 kHz). + #. Hardware may be corrupted, most products are from china and sometimes the quality is really poor. Contact your seller. + #. Newer versions of Mifare cards like DESFire/Ultralight maybe not work according to missing authentication, see `security`_ or different `protocol`_. + #. Some boards bought from Chinese manufactures do not use the best components and this can affect the detection of different types of tag/card. In some of these boards, the L1 and L2 inductors do not have a high enough current so the signal generated is not enough to get Ultralight C and NTAG203 tags to work, replacing those with same inductance (2.2uH) but higher operating current inductors should make things work smoothly. Also, in some of those boards the harmonic and matching circuit needs to be tuned, for this replace C4 and C5 with 33pf capacitors and you are all set. (Source: `Mikro Elektronika`_) + + +* **My mobile phone doesn't recognize the MFRC522** or **my MFRC522 can't read data from other MFRC522** + + #. Card simulation is not supported. + #. Communication with mobile phones is not supported. + #. Peer to peer communication is not supported. + + +* **I can only read the card UID.** + + #. Maybe the `AccessBits` have been accidentally set and now an unknown password is set. This can not be reverted. + #. Probably the card is encrypted. Especially official cards like public transport, university or library cards. There is *no* way to get access with this library. + + +* **Where do I get more information?** + + #. For general support from the community, see `Arduino Forum `_ or `StackOverflow `_ . + #. Visit the `community mfrc522 wiki `_ . + #. Read the datasheets! + #. Your preferred search engine. + + +* **I need more features.** + + #. If software: code it and make a pull request. + #. If hardware: buy a more expensive chip like the PN532 (supports NFC and many more, but costs about $15) + + +.. _license: +License +------- +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to https://unlicense.org/ + + +.. _dependency: +Dependency +---------- + +* **Arduino.h** + + * From: Arduino IDE / target specific + * License: (target: Arduino) GNU Lesser General Public License 2.1 + +* **SPI.h** + + * From: Arduino IDE / target specific + * License: (target: Arduino) GNU Lesser General Public License 2.1 + +* **stdint.h** + + * From: Arduino IDE / Compiler and target specific + * License: different + + +History +------- + +The MFRC522 library was first created in Jan 2012 by Miguel Balboa (from +http://circuitito.com) based on code by Dr. Leong (from http://B2CQSHOP.com) +for *"Arduino RFID module Kit 13.56 Mhz with Tags SPI W and R By COOQRobot"*. + +It was translated into English and rewritten/refactored in the fall of 2013 +by Søren Thing Andersen (from http://access.thing.dk). + +It has been extended with functionality to alter sector 0 on Chinese UID changeable MIFARE card in Oct 2014 by Tom Clement (from http://tomclement.nl). + +Maintained by miguelbalboa until 2016. +Maintained by Rotzbua from 2016 until 2022. + + +.. _arduino: https://arduino.cc/ +.. _ebay: https://www.ebay.com/ +.. _iso/iec 14443a: https://en.wikipedia.org/wiki/ISO/IEC_14443 +.. _iso/iec 14443-3\:2011 part 3: +.. _nxp mfrc522: https://www.nxp.com/documents/data_sheet/MFRC522.pdf +.. _broken: https://eprint.iacr.org/2008/166 +.. _supported by hardware: https://web.archive.org/web/20151210045625/http://www.nxp.com/documents/leaflet/939775017564.pdf +.. _Arduino forum: https://forum.arduino.cc +.. _stdint.h: https://en.wikibooks.org/wiki/C_Programming/C_Reference/stdint.h +.. _Mikro Elektronika: https://forum.mikroe.com/viewtopic.php?f=147&t=64203 diff --git a/libraries/MFRC522/UNLICENSE b/libraries/MFRC522/UNLICENSE new file mode 100644 index 0000000..efb9808 --- /dev/null +++ b/libraries/MFRC522/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/libraries/MFRC522/changes.txt b/libraries/MFRC522/changes.txt new file mode 100644 index 0000000..85946fd --- /dev/null +++ b/libraries/MFRC522/changes.txt @@ -0,0 +1,199 @@ +-- Add changes to unreleased tag until we make a release. + +xxxxx , v1.4.12 + +30 Dec 2023, v1.4.11 +- fix: documentation + +1 Nov 2021 , v1.4.10 +- fix: timeout on Non-AVR boards; feat: Use yield() in busy wait loops @greezybacon +- fix PCD problem after selftest + +31 Jul 2021, v1.4.9 +- Removed example AccessControl +- Updated docs + +30 Dec 2020, v1.4.8 +- Fixed wrong SPI clock speed. + +25 Jun 2020, v1.4.7 +- Fixed typos +- Moved PICC memory layout to /doc + +21 Jan 2020, v1.4.6 +- Library in freeze mode, no new features, no new examples, just bugfixes to keep compatibility for old projects +- Updated documentation +- Removed deprecation warnings due code freeze + +12 Jun 2019, v1.4.5 +- Library in freeze mode, no new features, no new examples, just bugfixes to keep compatibility for old projects +- Updated README + +31 Mar 2019, v1.4.4 +- Fixed example +- Fixed UnbrickUidSector +- Updated comments +- Removed teensy from CI and PlatformIO config + +27 Oct 2018, v1.4.3 +- Added counterfeit detection and hint about bad boards +- Improved hardware based reset + +04 Oct 2018, v1.4.2 +- Fixed keywords +- Changed development status of library + +04 Oct 2018, v1.4.1 +- Replaced UINT8_MAX by UNUSED_PIN +- Replaced default value NULL for pointer with correct nullptr +- Added pin layout for wemos d1 mini @mmone +- Added alternative fritzing layout for mfrc522 reader @jose1711 +- Added soft power control functions @Bill2462 +- Fixed typo in example ReadAndWrite.ino @Drachenfrucht1 +- Fixed grammar @KiddieD +- Fixed infinite loop in example AccessControl.ino @mads256c +- Fixed 0-index key ID equals to 0 fails in AccessControl.ino @thoaitrieu +- Changed type boolean to bool for a more common use @Rotzbua +- Fixed wrong var names in PICC_Select MFRC522.cpp @tuyethoa08041997 +- Fixed hang PCD_Init() on non-arduino boards @heziegl +- Fixed possible infinite loop PCD_Reset() @psgreco +- And many thanks to all other contributors + +13 Apr 2018, v1.4.0 +- Replaced UINT8_MAX by UNUSED_PIN @Rotzbua +- Fixed hang PCD_Init() on non-arduino boards @heziegl +- deprecate MIFARE_SetAccessBits @Rotzbua +- IMPORTANT: v1.4.X will be last version under license "unlicense" + +22 Mar 2017, v1.3.6 +- Added deprecate and compiler warnings @Rotzbua + +8 Apr 2017, v1.3.5 +- Updated "AccessControl.ino", bugs fixed and alternate wipe button polling method without using other library @beyondszine reviewed by @omersiar +- Updated README notice about port for esp8266 @mmmmar + +7 Apr 2017, v1.3.4 +- Added new example "rfid_read_personal_data.ino" @ryand1011 +- Updated example "rfid_write_personal_data.ino" code style to Arduino IDE @Rotzbua +- Removed (temp.) Teensy from travis build script because fails @Rotzbua + +26 Mar 2017, v1.3.3 +- Fixed bugs in MFRC522Extended, now should work with Desfire.h from JPG-Consulting @Rotzbua +- Fixed UINT8_MAX error @Rotzbua + +15 Mar 2017, v1.3.2 +- Added ESP8266 to library.properties + +15 Mar 2017, v1.3.1 +- Fixed compiler warning in MFRC522Extended @Rotzbua +- Removed unused function @Rotzbua + +13 Mar 2017, v1.3.0 +- Warning: Source has moved to folder src! +- Added addtional class to support ISO/IEC 14443-4 PICCs @JPG-Consulting +- Added RATS (Request for Answer To Select) @JPG-Consulting +- More information see https://github.com/miguelbalboa/rfid/pull/271 @JPG-Consulting + +13 Mar 2017, v1.2.1 +- Removed need for reset pin #275 @tkoester +- Added SPI speed option + Various minor changes #276 @tuyethoa08041997 +- Updated documentation, travis build script and small code change @Rotzbua + +3 Jan 2017, v1.2.0 +- Warning: This version introduce usage of stdint.h, usage of not well defined int types are abandoned. + Interface has changed: e.g. long -> int32_t + @Rotzbua +- Removed problematic example examples/servo_motor/servo_motor.ino @omersiar +- Added examples/AccessControl/AccessControl.ino @omersiar +- Fixed minor issues reported in #211 @omersiar +- Added bad components hint to README @danielcbit +- Fixed selftest @surr +- Fixed auth problem with long UIDs @surr + +26 Aug 2016, v1.1.9 +- Warning: Only Arduino IDE version 1.6 is supported, please update your IDE to 1.6 to use this Library. +- Added ESP8266 platform support @Rotzbua +- Changed README.rst content to show more info @Rotzbua +- Minor Changes to examples/ReadUidMultiReader/ReadUidMultiReader.ino example @Rotzbua + +11 Feb 2016, v1.1.8 +- Added examples/MinimalInterrupt/MinimalInterrupt.ino example, Interrupt example @lmmeng +- Added .gitignore file allows the project to be more easily used as a subproject. @BenWiederhake +- Added Added Teensy 2.0 & Tensy++ 2.0 pinouts to README.rst @jkutianski + +16 Jan 2016, v1.1.7 +- README.rst Spelling and Grammar Tweak @cuthbertnibbles +- Added examples/servo_motor/servo_motor.ino example, Arduino RFID Access Control with a Servo Motor @techied +- Added examples/RFID-Cloner/RFID-Cloner.ino Copy from rfid cards with standard authentication @stefanblommaert +- Fix compile error at examples/RFID-Cloner/RFID-Cloner.ino, using MFRC522:::PICC_Type, @Rotzbua + +06 Jan 2016, v1.1.6 +- Fixed compilation error for examples/ReadNUID/ReadNUID.ino example. @Rotzbua + +04 Jan 2016, v1.1.5 +- Use settings functions on SPI libraries, setSPIConfig was deleted, now the library use SPI.beginTransaction() and SPI.endTransaction() @sophiekovalevsky +- Added examples/ReadNUID/ReadNUID.ino example, showing how to read new NUID from a PICC to serial. @sophiekovalevsky + +03 Jan 2016, v1.1.4 +- Added Authentication with Ntag 213,215,216 returns the pACK MFRC522::PCD_NTAG216_AUTH @Gargantuanman +- Starting to use versions http://semver.org/ +- Continuous Integration @ivankravets +- functions return MFRC522::StatusCode and MFRC522::PICC_Type instead of generic byte @rotzbua +- removed int-values of MFRC522::StatusCode and MFRC522::PICC_Type @rotzbua + +05 Dec 2015 +- recognize infineon cards correctly @mayatforest +- added multi reader support, see example @lmmeng + +10 Nov 2014 +- Updated the changelog. +- Added makefile. + +24 Oct 2014 +- Added PlatformIO-based manifest file. + +17 Jul 2014 +- Written documentation for the library. +- Added rfid_default_keys example. + +11 Jun 2014 +- Updated example: ReadAndWrite. + +14 Apr 2014 +- Updated examples: DumpInfo, MifareClassicValueBlock, and ReadAndWrite. + +12 Feb 2014 +- Fixed resetPowerDownPin initial state. + +29 Jan 2014 +- Fixed chipSelectPin initial state. + +30 Nov 2013 +- Examples put in their own folders. +- Updated the keywords.txt file. + +12 Nov 2013 +- Updated examples: DumpInfo, MifareClassicValueBlock, and ReadAndWrite. + +20 Oct 2013 +- All constants, functions and parameters are now commented in English. +- Code refactored, most function names have changed. +- Support ISO-14443-3 anti collision and 4/7/10 byte UIDs (cascade levels). +- Added functions for MIFARE Classic Decrement/Increment/Restore/Transfer + and MIFARE Ultralight Write. +- New examples written. + +19 Oct 2013 +- Renamed library from RFID to MFRC522 (RFID seemed to generic). +- Register names changed to comply with datasheet. +- Global defines moved into class. + +24 Sep 2013 +- Turn off encryption when tag is halted. + +27 Jan 2013 +- Added README and small TODO list. +- Added example to show Serial on LCD display. + +09 Sep 2012 +- Initial commit to GitHub. diff --git a/libraries/MFRC522/doc/PICCMemoryLayout.md b/libraries/MFRC522/doc/PICCMemoryLayout.md new file mode 100644 index 0000000..944d905 --- /dev/null +++ b/libraries/MFRC522/doc/PICCMemoryLayout.md @@ -0,0 +1,67 @@ +# Memory Layout of common PICCs + +To read and write from MIFARE PICCs, the MIFARE protocol is used after the PICC has been selected. + +## Datasheet References + +The **MIFARE Classic** chips and protocol is described in the datasheets: + * 1K: https://www.mouser.com/ds/2/302/MF1S503x-89574.pdf + * 4K: https://datasheet.octopart.com/MF1S7035DA4,118-NXP-Semiconductors-datasheet-11046188.pdf + * Mini: http://www.idcardmarket.com/download/mifare_S20_datasheet.pdf + +The **MIFARE Ultralight** chip and protocol is described in the datasheets: + * Ultralight: https://www.nxp.com/documents/data_sheet/MF0ICU1.pdf + * Ultralight C: https://www.nxp.com/documents/short_data_sheet/MF0ICU2_SDS.pdf + +## MIFARE Classic 1K (MF1S503x) + +Has 16 sectors4 blocks/sector16 bytes/block = 1024 bytes. + + The blocks are numbered 0-63. + Block 3 in each sector is the Sector Trailer. See https://www.mouser.com/ds/2/302/MF1S503x-89574.pdf sections 8.6 and 8.7: + Bytes 0-5: Key A + Bytes 6-8: Access Bits + Bytes 9: User data + Bytes 10-15: Key B (or user data) + Block 0 is read-only manufacturer data. + To access a block, an authentication using a key from the block's sector must be performed first. + Example: To read from block 10, first authenticate using a key from sector 3 (blocks 8-11). + All keys are set to FFFFFFFFFFFFh at chip delivery. + Warning: Please read section 8.7 "Memory Access". It includes this text: if the PICC detects a format violation the whole sector is irreversibly blocked. + To use a block in "value block" mode (for Increment/Decrement operations) you need to change the sector trailer. Use PICC_SetAccessBits() to calculate the bit patterns. + +## MIFARE Classic 4K (MF1S703x): + +Has (32 sectors4 blocks/sector + 8 sectors16 blocks/sector)16 bytes/block = 4096 bytes. + + The blocks are numbered 0-255. + The last block in each sector is the Sector Trailer like above. + +## MIFARE Classic Mini (MF1 IC S20): + +Has 5 sectors4 blocks/sector16 bytes/block = 320 bytes. + + The blocks are numbered 0-19. + The last block in each sector is the Sector Trailer like above. + +## MIFARE Ultralight (MF0ICU1): + +Has 16 pages of 4 bytes = 64 bytes. + + Pages 0 + 1 is used for the 7-byte UID. + Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see https://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + Pages 4-15 are read/write unless blocked by the lock bytes in page 2. + +## MIFARE Ultralight C (MF0ICU2): + +Has 48 pages of 4 bytes = 192 bytes. + + Pages 0 + 1 is used for the 7-byte UID. + Page 2 contains the last check digit for the UID, one byte manufacturer internal data, and the lock bytes (see https://www.nxp.com/documents/data_sheet/MF0ICU1.pdf section 8.5.2) + Page 3 is OTP, One Time Programmable bits. Once set to 1 they cannot revert to 0. + Pages 4-39 are read/write unless blocked by the lock bytes in page 2. + Page 40 Lock bytes + Page 41 16 bit one way counter + Pages 42-43 Authentication configuration + Pages 44-47 Authentication key \ No newline at end of file diff --git a/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz b/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz new file mode 100644 index 0000000..7642ac2 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.fzz differ diff --git a/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png b/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png new file mode 100644 index 0000000..e9af1ab Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/Arduino-Uno-r3-with-RFID-RC522.png differ diff --git a/libraries/MFRC522/doc/fritzing/RFID-RC522 - Pin Layout.png b/libraries/MFRC522/doc/fritzing/RFID-RC522 - Pin Layout.png new file mode 100644 index 0000000..5b4f842 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/RFID-RC522 - Pin Layout.png differ diff --git a/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.fzpz b/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.fzpz new file mode 100644 index 0000000..1866b04 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.fzpz differ diff --git a/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.png b/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.png new file mode 100644 index 0000000..4444909 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/RFID-RC522-v2.png differ diff --git a/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.fzpz b/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.fzpz new file mode 100644 index 0000000..43da829 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.fzpz differ diff --git a/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.png b/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.png new file mode 100644 index 0000000..d56c6d5 Binary files /dev/null and b/libraries/MFRC522/doc/fritzing/RFID-RC522-v3.png differ diff --git a/libraries/MFRC522/doc/rfidmifare.doc b/libraries/MFRC522/doc/rfidmifare.doc new file mode 100644 index 0000000..a2f780b Binary files /dev/null and b/libraries/MFRC522/doc/rfidmifare.doc differ diff --git a/libraries/MFRC522/doc/rfidmifare.pdf b/libraries/MFRC522/doc/rfidmifare.pdf new file mode 100644 index 0000000..2b85aa6 Binary files /dev/null and b/libraries/MFRC522/doc/rfidmifare.pdf differ diff --git a/libraries/MFRC522/examples/ChangeUID/ChangeUID.ino b/libraries/MFRC522/examples/ChangeUID/ChangeUID.ino new file mode 100644 index 0000000..eb7b8c8 --- /dev/null +++ b/libraries/MFRC522/examples/ChangeUID/ChangeUID.ino @@ -0,0 +1,112 @@ +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example to change UID of changeable MIFARE card. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * This sample shows how to set the UID on a UID changeable MIFARE card. + * + * @author Tom Clement + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +/* Set your new UID here! */ +#define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF} + +MFRC522::MIFARE_Key key; + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Warning: this example overwrites the UID of your UID changeable card, use with care!")); + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } +} + +// Setting the UID can be as simple as this: +//void loop() { +// byte newUid[] = NEW_UID; +// if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) { +// Serial.println("Wrote new UID to card."); +// } +// delay(1000); +//} + +// But of course this is a more proper approach +void loop() { + + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. And if present, select one. + if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { + delay(50); + return; + } + + // Now a card is selected. The UID and SAK is in mfrc522.uid. + + // Dump UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < mfrc522.uid.size; i++) { + Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); + Serial.print(mfrc522.uid.uidByte[i], HEX); + } + Serial.println(); + + // Dump PICC type +// MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); +// Serial.print(F("PICC type: ")); +// Serial.print(mfrc522.PICC_GetTypeName(piccType)); +// Serial.print(F(" (SAK ")); +// Serial.print(mfrc522.uid.sak); +// Serial.print(")\r\n"); +// if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI +// && piccType != MFRC522::PICC_TYPE_MIFARE_1K +// && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { +// Serial.println(F("This sample only works with MIFARE Classic cards.")); +// return; +// } + + // Set new UID + byte newUid[] = NEW_UID; + if ( mfrc522.MIFARE_SetUid(newUid, (byte)4, true) ) { + Serial.println(F("Wrote new UID to card.")); + } + + // Halt PICC and re-select it so DumpToSerial doesn't get confused + mfrc522.PICC_HaltA(); + if ( ! mfrc522.PICC_IsNewCardPresent() || ! mfrc522.PICC_ReadCardSerial() ) { + return; + } + + // Dump the new memory contents + Serial.println(F("New UID and contents:")); + mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); + + delay(2000); +} diff --git a/libraries/MFRC522/examples/DumpInfo/DumpInfo.ino b/libraries/MFRC522/examples/DumpInfo/DumpInfo.ino new file mode 100644 index 0000000..1a799e0 --- /dev/null +++ b/libraries/MFRC522/examples/DumpInfo/DumpInfo.ino @@ -0,0 +1,69 @@ +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program showing how to read data from a PICC to serial. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * Example sketch/program showing how to read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID + * Reader on the Arduino SPI interface. + * + * When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE + * then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When + * you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output + * will show the ID/UID, type and any data blocks it can read. Note: you may see "Timeout in communication" messages + * when removing the PICC from reading distance too early. + * + * If your reader supports it, this sketch/program will read all the PICCs presented (that is: multiple tag reading). + * So if you stack two or more PICCs on top of each other and present them to the reader, it will first output all + * details of the first and then the next PICC. Note that this may take some time as all data blocks are dumped, so + * keep the PICCs at reading distance until complete. + * + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 + delay(4); // Optional delay. Some board do need more time after init to be ready, see Readme + mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details + Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks...")); +} + +void loop() { + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) { + return; + } + + // Dump debug info about the card; PICC_HaltA() is automatically called + mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); +} diff --git a/libraries/MFRC522/examples/FixBrickedUID/FixBrickedUID.ino b/libraries/MFRC522/examples/FixBrickedUID/FixBrickedUID.ino new file mode 100644 index 0000000..8530d41 --- /dev/null +++ b/libraries/MFRC522/examples/FixBrickedUID/FixBrickedUID.ino @@ -0,0 +1,55 @@ +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program to fix a broken UID changeable MIFARE cards. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * This sample shows how to fix a broken UID changeable MIFARE cards that have a corrupted sector 0. + * + * @author Tom Clement + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +MFRC522::MIFARE_Key key; + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Warning: this example clears your mifare UID, use with care!")); + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } +} + +void loop() { + if ( mfrc522.MIFARE_UnbrickUidSector(false) ) { + Serial.println(F("Cleared sector 0, set UID to 1234. Card should be responsive again now.")); + } + delay(1000); +} diff --git a/libraries/MFRC522/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino b/libraries/MFRC522/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino new file mode 100644 index 0000000..21359b0 --- /dev/null +++ b/libraries/MFRC522/examples/MifareClassicValueBlock/MifareClassicValueBlock.ino @@ -0,0 +1,321 @@ +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * This sample shows how to setup blocks on a MIFARE Classic PICC (= card/tag) + * to be in "Value Block" mode: in this mode the operations Increment/Decrement, + * Restore and Transfer can be used. + * + * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +MFRC522::MIFARE_Key key; + +/** + * Initialize. + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + + // Prepare the key (used both as key A and as key B) + // using FFFFFFFFFFFFh which is the default at chip delivery from the factory + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + Serial.println(F("Scan a MIFARE Classic PICC to demonstrate Value Block mode.")); + Serial.print(F("Using key (for A and B):")); + dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + + Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1")); +} + +/** + * Main loop. + */ +void loop() { + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Check for compatibility + if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI + && piccType != MFRC522::PICC_TYPE_MIFARE_1K + && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { + Serial.println(F("This sample only works with MIFARE Classic cards.")); + return; + } + + // In this sample we use the second sector, + // that is: sector #1, covering block #4 up to and including block #7 + byte sector = 1; + byte valueBlockA = 5; + byte valueBlockB = 6; + byte trailerBlock = 7; + MFRC522::StatusCode status; + byte buffer[18]; + byte size = sizeof(buffer); + int32_t value; + + // Authenticate using key A + Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Show the whole sector as it currently is + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // We need a sector trailer that defines blocks 5 and 6 as Value Blocks and enables key B + // The last block in a sector (block #3 for Mifare Classic 1K) is the Sector Trailer. + // See http://www.nxp.com/documents/data_sheet/MF1S503x.pdf sections 8.6 and 8.7: + // Bytes 0-5: Key A + // Bytes 6-8: Access Bits + // Bytes 9: User data + // Bytes 10-15: Key B (or user data) + byte trailerBuffer[] = { + 255, 255, 255, 255, 255, 255, // Keep default key A + 0, 0, 0, + 0, + 255, 255, 255, 255, 255, 255}; // Keep default key B + // The access bits are stored in a peculiar fashion. + // There are four groups: + // g[0] Access bits for block 0 (for sectors 0-31) + // or blocks 0-4 (for sectors 32-39) + // g[1] Access bits for block 1 (for sectors 0-31) + // or blocks 5-9 (for sectors 32-39) + // g[2] Access bits for block 2 (for sectors 0-31) + // or blocks 10-14 (for sectors 32-39) + // g[3] Access bits for the Sector Trailer: block 3 (for sectors 0-31) + // or block 15 (for sectors 32-39) + // Each group has access bits [C1 C2 C3], in this code C1 is MSB and C3 is LSB. + // Determine the bit pattern needed using MIFARE_SetAccessBits: + // g0=0 access bits for block 0 (of this sector) using [0 0 0] = 000b = 0 + // which means key A|B have r/w for block 0 of this sector + // which (in this example) translates to block #4 within sector #1; + // this is the transport configuration (at factory delivery). + // g1=6 access bits for block 1 (of this sector) using [1 1 0] = 110b = 6 + // which means block 1 (of this sector) is used as a value block, + // which (in this example) translates to block #5 within sector #1; + // where key A|B have r, key B has w, key B can increment, + // and key A|B can decrement, transfer, and restore. + // g2=6 same thing for block 2 (of this sector): set it to a value block; + // which (in this example) translates to block #6 within sector #1; + // g3=3 access bits for block 3 (of this sector): the Sector Trailer here; + // using [0 1 1] = 011b = 3 which means only key B has r/w access + // to the Sector Trailer (block 3 of this sector) from now on + // which (in this example) translates to block #7 within sector #1; + mfrc522.MIFARE_SetAccessBits(&trailerBuffer[6], 0, 6, 6, 3); + + // Read the sector trailer as it is currently stored on the PICC + Serial.println(F("Reading sector trailer...")); + status = mfrc522.MIFARE_Read(trailerBlock, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Check if it matches the desired access pattern already; + // because if it does, we don't need to write it again... + if ( buffer[6] != trailerBuffer[6] + || buffer[7] != trailerBuffer[7] + || buffer[8] != trailerBuffer[8]) { + // They don't match (yet), so write it to the PICC + Serial.println(F("Writing new sector trailer...")); + status = mfrc522.MIFARE_Write(trailerBlock, trailerBuffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + } + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // A value block has a 32 bit signed value stored three times + // and an 8 bit address stored 4 times. Make sure that valueBlockA + // and valueBlockB have that format (note that it will only format + // the block when it doesn't comply to the expected format already). + formatValueBlock(valueBlockA); + formatValueBlock(valueBlockB); + + // Add 1 to the value of valueBlockA and store the result in valueBlockA. + Serial.print("Adding 1 to value of block "); Serial.println(valueBlockA); + status = mfrc522.MIFARE_Increment(valueBlockA, 1); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Increment() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + status = mfrc522.MIFARE_Transfer(valueBlockA); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Transfer() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Show the new value of valueBlockA + status = mfrc522.MIFARE_GetValue(valueBlockA, &value); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_GetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + Serial.print("New value of value block "); Serial.print(valueBlockA); + Serial.print(" = "); Serial.println(value); + + // Decrement 10 from the value of valueBlockB and store the result in valueBlockB. + Serial.print("Subtracting 10 from value of block "); Serial.println(valueBlockB); + status = mfrc522.MIFARE_Decrement(valueBlockB, 10); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Decrement() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + status = mfrc522.MIFARE_Transfer(valueBlockB); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Transfer() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + // Show the new value of valueBlockB + status = mfrc522.MIFARE_GetValue(valueBlockB, &value); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_GetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + Serial.print(F("New value of value block ")); Serial.print(valueBlockB); + Serial.print(F(" = ")); Serial.println(value); + // Check some boundary... + if (value <= -100) { + Serial.println(F("Below -100, so resetting it to 255 = 0xFF just for fun...")); + status = mfrc522.MIFARE_SetValue(valueBlockB, 255); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("mifare_SetValue() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + } + + // Dump the sector data + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Halt PICC + mfrc522.PICC_HaltA(); + // Stop encryption on PCD + mfrc522.PCD_StopCrypto1(); +} + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} + +/** + * Ensure that a given block is formatted as a Value Block. + */ +void formatValueBlock(byte blockAddr) { + byte buffer[18]; + byte size = sizeof(buffer); + MFRC522::StatusCode status; + + Serial.print(F("Reading block ")); Serial.println(blockAddr); + status = mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + if ( (buffer[0] == (byte)~buffer[4]) + && (buffer[1] == (byte)~buffer[5]) + && (buffer[2] == (byte)~buffer[6]) + && (buffer[3] == (byte)~buffer[7]) + + && (buffer[0] == buffer[8]) + && (buffer[1] == buffer[9]) + && (buffer[2] == buffer[10]) + && (buffer[3] == buffer[11]) + + && (buffer[12] == (byte)~buffer[13]) + && (buffer[12] == buffer[14]) + && (buffer[12] == (byte)~buffer[15])) { + Serial.println(F("Block has correct Value Block format.")); + } + else { + Serial.println(F("Formatting as Value Block...")); + byte valueBlock[] = { + 0, 0, 0, 0, + 255, 255, 255, 255, + 0, 0, 0, 0, + blockAddr, ~blockAddr, blockAddr, ~blockAddr }; + status = mfrc522.MIFARE_Write(blockAddr, valueBlock, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + } +} diff --git a/libraries/MFRC522/examples/MinimalInterrupt/MinimalInterrupt.ino b/libraries/MFRC522/examples/MinimalInterrupt/MinimalInterrupt.ino new file mode 100644 index 0000000..37ec436 --- /dev/null +++ b/libraries/MFRC522/examples/MinimalInterrupt/MinimalInterrupt.ino @@ -0,0 +1,139 @@ +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * Minimal example how to use the interrupts to read the UID of a MIFARE Classic PICC + * (= card/tag). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 3 10 + * IRQ ? ? ? ? 2 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 3 // Configurable, see typical pin layout above +#define IRQ_PIN 2 // Configurable, depends on hardware + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +MFRC522::MIFARE_Key key; + +volatile bool bNewInt = false; +byte regVal = 0x7F; +void activateRec(MFRC522 mfrc522); +void clearInt(MFRC522 mfrc522); + +/** + * Initialize. + */ +void setup() { + Serial.begin(115200); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + + mfrc522.PCD_Init(); // Init MFRC522 card + + /* read and printout the MFRC522 version (valid values 0x91 & 0x92)*/ + Serial.print(F("Ver: 0x")); + byte readReg = mfrc522.PCD_ReadRegister(mfrc522.VersionReg); + Serial.println(readReg, HEX); + + /* setup the IRQ pin*/ + pinMode(IRQ_PIN, INPUT_PULLUP); + + /* + * Allow the ... irq to be propagated to the IRQ pin + * For test purposes propagate the IdleIrq and loAlert + */ + regVal = 0xA0; //rx irq + mfrc522.PCD_WriteRegister(mfrc522.ComIEnReg, regVal); + + bNewInt = false; //interrupt flag + + /*Activate the interrupt*/ + attachInterrupt(digitalPinToInterrupt(IRQ_PIN), readCard, FALLING); + + do { //clear a spourious interrupt at start + ; + } while (!bNewInt); + bNewInt = false; + + Serial.println(F("End setup")); +} + +/** + * Main loop. + */ +void loop() { + if (bNewInt) { //new read interrupt + Serial.print(F("Interrupt. ")); + mfrc522.PICC_ReadCardSerial(); //read the tag data + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + + clearInt(mfrc522); + mfrc522.PICC_HaltA(); + bNewInt = false; + } + + // The receiving block needs regular retriggering (tell the tag it should transmit??) + // (mfrc522.PCD_WriteRegister(mfrc522.FIFODataReg,mfrc522.PICC_CMD_REQA);) + activateRec(mfrc522); + delay(100); +} //loop() + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} +/** + * MFRC522 interrupt serving routine + */ +void readCard() { + bNewInt = true; +} + +/* + * The function sending to the MFRC522 the needed commands to activate the reception + */ +void activateRec(MFRC522 mfrc522) { + mfrc522.PCD_WriteRegister(mfrc522.FIFODataReg, mfrc522.PICC_CMD_REQA); + mfrc522.PCD_WriteRegister(mfrc522.CommandReg, mfrc522.PCD_Transceive); + mfrc522.PCD_WriteRegister(mfrc522.BitFramingReg, 0x87); +} + +/* + * The function to clear the pending interrupt bits after interrupt serving routine + */ +void clearInt(MFRC522 mfrc522) { + mfrc522.PCD_WriteRegister(mfrc522.ComIrqReg, 0x7F); +} + diff --git a/libraries/MFRC522/examples/Ntag216_AUTH/Ntag216_AUTH.ino b/libraries/MFRC522/examples/Ntag216_AUTH/Ntag216_AUTH.ino new file mode 100644 index 0000000..b2f3905 --- /dev/null +++ b/libraries/MFRC522/examples/Ntag216_AUTH/Ntag216_AUTH.ino @@ -0,0 +1,62 @@ +/* + * This example show how you can get Authenticated by the NTAG213,215,216. By default the tags are unprotected in order to protect them we need to write 4 different values: + * Using mfrc522.MIFARE_Ultralight_Write(PageNum, Data, #Databytes)) + * 1.- We need to write the 32bit passWord to page 0xE5 !for ntag 213 and 215 page is different refer to nxp documentation! + * 2.- Now Write the 16 bits pACK to the page 0xE6 use the 2 high bytes like this: pACKH + pACKL + 00 + 00 after an authentication the tag will return this secret bytes + * 3.- Now we need to write the first page we want to protect this is a 1 byte data in page 0xE3 we need to write 00 + 00 + 00 + firstPage all pages after this one are write protected + * Now WRITE protection is ACTIVATED so we need to get authenticated in order to write the last data + * 4.- Finally we need to write an access record in order to READ protect the card this step is optional only if you want to read protect also write 80 + 00 + 00 + 00 to 0xE4 + * After completing all these steps you will nee to authenticate first in order to read or write ant page after the first page you selected to protect. + * To disengage protection just write the page (0xE3) to 00 + 00 + 00 + FF that going to remove all protection. + * + * @author GARGANTUA from RoboCreators.com & paradoxalabs.com + * @license Released into the public domain. + */ + +#include +#include + +#define RST_PIN 9 // +#define SS_PIN 10 // + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 + Serial.println(F("Scan PICC to see UID, type, and data blocks...")); +} + +void loop() { + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) { + return; + } + + byte PSWBuff[] = {0xFF, 0xFF, 0xFF, 0xFF}; // 32 bit password default FFFFFFFF. + byte pACK[] = {0, 0}; // 16 bit password ACK returned by the NFCtag. + + Serial.print("Auth: "); + Serial.println(mfrc522.PCD_NTAG216_AUTH(&PSWBuff[0], pACK)); // Request authentification if return STATUS_OK we are good. + + //Print PassWordACK + Serial.print(pACK[0], HEX); + Serial.println(pACK[1], HEX); + + byte WBuff[] = {0x00, 0x00, 0x00, 0x04}; + byte RBuff[18]; + + //Serial.print("CHG BLK: "); + //Serial.println(mfrc522.MIFARE_Ultralight_Write(0xE3, WBuff, 4)); // How to write to a page. + + mfrc522.PICC_DumpMifareUltralightToSerial(); // This is a modifier dump just change the for circle to < 232 instead of < 16 in order to see all the pages on NTAG216. + + delay(3000); +} diff --git a/libraries/MFRC522/examples/RFID-Cloner/RFID-Cloner.ino b/libraries/MFRC522/examples/RFID-Cloner/RFID-Cloner.ino new file mode 100644 index 0000000..2a0986a --- /dev/null +++ b/libraries/MFRC522/examples/RFID-Cloner/RFID-Cloner.ino @@ -0,0 +1,314 @@ +/* + * Copy the RFID card data into variables and then + * scan the second empty card to copy all the data + * ---------------------------------------------------------------------------- + * Example sketch/program which will try the most used default keys listed in + * https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys to dump the + * block 0 of a MIFARE RFID card using a RFID-RC522 reader. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +byte buffer[18]; +byte block; +byte waarde[64][16]; +MFRC522::StatusCode status; + +MFRC522::MIFARE_Key key; + +// Number of known default keys (hard-coded) +// NOTE: Synchronize the NR_KNOWN_KEYS define with the defaultKeys[] array +#define NR_KNOWN_KEYS 8 +// Known keys, see: https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys +byte knownKeys[NR_KNOWN_KEYS][MFRC522::MF_KEY_SIZE] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FF FF FF FF FF FF = factory default + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // A0 A1 A2 A3 A4 A5 + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, // B0 B1 B2 B3 B4 B5 + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, // 4D 3A 99 C3 51 DD + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, // 1A 98 2C 7E 45 9A + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // D3 F7 D3 F7 D3 F7 + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, // AA BB CC DD EE FF + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // 00 00 00 00 00 00 +}; + +char choice; +/* + * Initialize. + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Try the most used default keys to print block 0 to 63 of a MIFARE PICC.")); + Serial.println("1.Read card \n2.Write to card \n3.Copy the data."); + + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } +} + + + + //Via seriele monitor de bytes uitlezen in hexadecimaal + +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} +//Via seriele monitor de bytes uitlezen in ASCI + +void dump_byte_array1(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.write(buffer[i]); + } +} + +/* + * Try using the PICC (the tag/card) with the given key to access block 0 to 63. + * On success, it will show the key details, and dump the block data on Serial. + * + * @return true when the given key worked, false otherwise. + */ + +bool try_key(MFRC522::MIFARE_Key *key) +{ + bool result = false; + + for(byte block = 0; block < 64; block++){ + + // Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Read block + byte byteCount = sizeof(buffer); + status = mfrc522.MIFARE_Read(block, buffer, &byteCount); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + else { + // Successful read + result = true; + Serial.print(F("Success with key:")); + dump_byte_array((*key).keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + + // Dump block data + Serial.print(F("Block ")); Serial.print(block); Serial.print(F(":")); + dump_byte_array1(buffer, 16); //omzetten van hex naar ASCI + Serial.println(); + + for (int p = 0; p < 16; p++) //De 16 bits uit de block uitlezen + { + waarde [block][p] = buffer[p]; + Serial.print(waarde[block][p]); + Serial.print(" "); + } + + } + } + Serial.println(); + + Serial.println("1.Read card \n2.Write to card \n3.Copy the data."); + + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + return result; + + start(); +} + +/* + * Main loop. + */ +void loop() { + start(); + +} + +void start(){ + choice = Serial.read(); + + if(choice == '1') + { + Serial.println("Read the card"); + keuze1(); + + } + else if(choice == '2') + { + Serial.println("See what is in the variables"); + keuze2(); + } + else if(choice == '3') + { + Serial.println("Copying the data on to the new card"); + keuze3(); + } +} + +void keuze2(){ //Test waardes in blokken + + for(block = 4; block <= 62; block++){ + if(block == 7 || block == 11 || block == 15 || block == 19 || block == 23 || block == 27 || block == 31 || block == 35 || block == 39 || block == 43 || block == 47 || block == 51 || block == 55 || block == 59){ + block ++; + } + + Serial.print(F("Writing data into block ")); + Serial.print(block); + Serial.println("\n"); + + for(int j = 0; j < 16; j++){ + Serial.print(waarde[block][j]); + Serial.print(" "); + } + Serial.println("\n"); + + } + + Serial.println("1.Read card \n2.Write to card \n3.Copy the data."); + start(); +} + +void keuze3(){ //Copy the data in the new card +Serial.println("Insert new card..."); + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Try the known default keys + /*MFRC522::MIFARE_Key key; + for (byte k = 0; k < NR_KNOWN_KEYS; k++) { + // Copy the known key into the MIFARE_Key structure + for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { + key.keyByte[i] = knownKeys[k][i]; + } + }*/ + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + for(int i = 4; i <= 62; i++){ //De blocken 4 tot 62 kopieren, behalve al deze onderstaande blocken (omdat deze de authenticatie blokken zijn) + if(i == 7 || i == 11 || i == 15 || i == 19 || i == 23 || i == 27 || i == 31 || i == 35 || i == 39 || i == 43 || i == 47 || i == 51 || i == 55 || i == 59){ + i++; + } + block = i; + + // Authenticate using key A + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write data to the block + Serial.print(F("Writing data into block ")); + Serial.print(block); + Serial.println("\n"); + + dump_byte_array(waarde[block], 16); + + + status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(block, waarde[block], 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + + + Serial.println("\n"); + + } + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + + Serial.println("1.Read card \n2.Write to card \n3.Copy the data."); + start(); +} + +void keuze1(){ //Read card + Serial.println("Insert card..."); + // Look for new cards + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Try the known default keys + MFRC522::MIFARE_Key key; + for (byte k = 0; k < NR_KNOWN_KEYS; k++) { + // Copy the known key into the MIFARE_Key structure + for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { + key.keyByte[i] = knownKeys[k][i]; + } + // Try the key + if (try_key(&key)) { + // Found and reported on the key and block, + // no need to try other keys for this PICC + break; + } + } +} diff --git a/libraries/MFRC522/examples/ReadAndWrite/ReadAndWrite.ino b/libraries/MFRC522/examples/ReadAndWrite/ReadAndWrite.ino new file mode 100644 index 0000000..ab05e68 --- /dev/null +++ b/libraries/MFRC522/examples/ReadAndWrite/ReadAndWrite.ino @@ -0,0 +1,202 @@ +/** + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * This sample shows how to read and write data blocks on a MIFARE Classic PICC + * (= card/tag). + * + * BEWARE: Data will be written to the PICC, in sector #1 (blocks #4 to #7). + * + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +MFRC522::MIFARE_Key key; + +/** + * Initialize. + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + + // Prepare the key (used both as key A and as key B) + // using FFFFFFFFFFFFh which is the default at chip delivery from the factory + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + Serial.println(F("Scan a MIFARE Classic PICC to demonstrate read and write.")); + Serial.print(F("Using key (for A and B):")); + dump_byte_array(key.keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + + Serial.println(F("BEWARE: Data will be written to the PICC, in sector #1")); +} + +/** + * Main loop. + */ +void loop() { + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Check for compatibility + if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI + && piccType != MFRC522::PICC_TYPE_MIFARE_1K + && piccType != MFRC522::PICC_TYPE_MIFARE_4K) { + Serial.println(F("This sample only works with MIFARE Classic cards.")); + return; + } + + // In this sample we use the second sector, + // that is: sector #1, covering block #4 up to and including block #7 + byte sector = 1; + byte blockAddr = 4; + byte dataBlock[] = { + 0x01, 0x02, 0x03, 0x04, // 1, 2, 3, 4, + 0x05, 0x06, 0x07, 0x08, // 5, 6, 7, 8, + 0x09, 0x0a, 0xff, 0x0b, // 9, 10, 255, 11, + 0x0c, 0x0d, 0x0e, 0x0f // 12, 13, 14, 15 + }; + byte trailerBlock = 7; + MFRC522::StatusCode status; + byte buffer[18]; + byte size = sizeof(buffer); + + // Authenticate using key A + Serial.println(F("Authenticating using key A...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Show the whole sector as it currently is + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Read data from the block + Serial.print(F("Reading data from block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":")); + dump_byte_array(buffer, 16); Serial.println(); + Serial.println(); + + // Authenticate using key B + Serial.println(F("Authenticating again using key B...")); + status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write data to the block + Serial.print(F("Writing data into block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + dump_byte_array(dataBlock, 16); Serial.println(); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.println(); + + // Read data from the block (again, should now be what we have written) + Serial.print(F("Reading data from block ")); Serial.print(blockAddr); + Serial.println(F(" ...")); + status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + } + Serial.print(F("Data in block ")); Serial.print(blockAddr); Serial.println(F(":")); + dump_byte_array(buffer, 16); Serial.println(); + + // Check that data in block is what we have written + // by counting the number of bytes that are equal + Serial.println(F("Checking result...")); + byte count = 0; + for (byte i = 0; i < 16; i++) { + // Compare buffer (= what we've read) with dataBlock (= what we've written) + if (buffer[i] == dataBlock[i]) + count++; + } + Serial.print(F("Number of bytes that match = ")); Serial.println(count); + if (count == 16) { + Serial.println(F("Success :-)")); + } else { + Serial.println(F("Failure, no match :-(")); + Serial.println(F(" perhaps the write didn't work properly...")); + } + Serial.println(); + + // Dump the sector data + Serial.println(F("Current data in sector:")); + mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector); + Serial.println(); + + // Halt PICC + mfrc522.PICC_HaltA(); + // Stop encryption on PCD + mfrc522.PCD_StopCrypto1(); +} + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} diff --git a/libraries/MFRC522/examples/ReadNUID/ReadNUID.ino b/libraries/MFRC522/examples/ReadNUID/ReadNUID.ino new file mode 100644 index 0000000..ffc924e --- /dev/null +++ b/libraries/MFRC522/examples/ReadNUID/ReadNUID.ino @@ -0,0 +1,129 @@ +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program showing how to read new NUID from a PICC to serial. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * Example sketch/program showing how to the read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID + * Reader on the Arduino SPI interface. + * + * When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE + * then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When + * you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output + * will show the type, and the NUID if a new card has been detected. Note: you may see "Timeout in communication" messages + * when removing the PICC from reading distance too early. + * + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + */ + +#include +#include + +#define SS_PIN 10 +#define RST_PIN 9 + +MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class + +MFRC522::MIFARE_Key key; + +// Init array that will store new NUID +byte nuidPICC[4]; + +void setup() { + Serial.begin(9600); + SPI.begin(); // Init SPI bus + rfid.PCD_Init(); // Init MFRC522 + + for (byte i = 0; i < 6; i++) { + key.keyByte[i] = 0xFF; + } + + Serial.println(F("This code scan the MIFARE Classsic NUID.")); + Serial.print(F("Using the following key:")); + printHex(key.keyByte, MFRC522::MF_KEY_SIZE); +} + +void loop() { + + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! rfid.PICC_IsNewCardPresent()) + return; + + // Verify if the NUID has been readed + if ( ! rfid.PICC_ReadCardSerial()) + return; + + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = rfid.PICC_GetType(rfid.uid.sak); + Serial.println(rfid.PICC_GetTypeName(piccType)); + + // Check is the PICC of Classic MIFARE type + if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI && + piccType != MFRC522::PICC_TYPE_MIFARE_1K && + piccType != MFRC522::PICC_TYPE_MIFARE_4K) { + Serial.println(F("Your tag is not of type MIFARE Classic.")); + return; + } + + if (rfid.uid.uidByte[0] != nuidPICC[0] || + rfid.uid.uidByte[1] != nuidPICC[1] || + rfid.uid.uidByte[2] != nuidPICC[2] || + rfid.uid.uidByte[3] != nuidPICC[3] ) { + Serial.println(F("A new card has been detected.")); + + // Store NUID into nuidPICC array + for (byte i = 0; i < 4; i++) { + nuidPICC[i] = rfid.uid.uidByte[i]; + } + + Serial.println(F("The NUID tag is:")); + Serial.print(F("In hex: ")); + printHex(rfid.uid.uidByte, rfid.uid.size); + Serial.println(); + Serial.print(F("In dec: ")); + printDec(rfid.uid.uidByte, rfid.uid.size); + Serial.println(); + } + else Serial.println(F("Card read previously.")); + + // Halt PICC + rfid.PICC_HaltA(); + + // Stop encryption on PCD + rfid.PCD_StopCrypto1(); +} + + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void printHex(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} + +/** + * Helper routine to dump a byte array as dec values to Serial. + */ +void printDec(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(' '); + Serial.print(buffer[i], DEC); + } +} diff --git a/libraries/MFRC522/examples/ReadUidMultiReader/ReadUidMultiReader.ino b/libraries/MFRC522/examples/ReadUidMultiReader/ReadUidMultiReader.ino new file mode 100644 index 0000000..348a374 --- /dev/null +++ b/libraries/MFRC522/examples/ReadUidMultiReader/ReadUidMultiReader.ino @@ -0,0 +1,99 @@ +/** + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program showing how to read data from more than one PICC to serial. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * Example sketch/program showing how to read data from more than one PICC (that is: a RFID Tag or Card) using a + * MFRC522 based RFID Reader on the Arduino SPI interface. + * + * Warning: This may not work! Multiple devices at one SPI are difficult and cause many trouble!! Engineering skill + * and knowledge are required! + * + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS 1 SDA(SS) ** custom, take a unused pin, only HIGH/LOW required ** + * SPI SS 2 SDA(SS) ** custom, take a unused pin, only HIGH/LOW required ** + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_1_PIN 10 // Configurable, take a unused pin, only HIGH/LOW required, must be different to SS 2 +#define SS_2_PIN 8 // Configurable, take a unused pin, only HIGH/LOW required, must be different to SS 1 + +#define NR_OF_READERS 2 + +byte ssPins[] = {SS_1_PIN, SS_2_PIN}; + +MFRC522 mfrc522[NR_OF_READERS]; // Create MFRC522 instance. + +/** + * Initialize. + */ +void setup() { + + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + + SPI.begin(); // Init SPI bus + + for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) { + mfrc522[reader].PCD_Init(ssPins[reader], RST_PIN); // Init each MFRC522 card + Serial.print(F("Reader ")); + Serial.print(reader); + Serial.print(F(": ")); + mfrc522[reader].PCD_DumpVersionToSerial(); + } +} + +/** + * Main loop. + */ +void loop() { + + for (uint8_t reader = 0; reader < NR_OF_READERS; reader++) { + // Look for new cards + + if (mfrc522[reader].PICC_IsNewCardPresent() && mfrc522[reader].PICC_ReadCardSerial()) { + Serial.print(F("Reader ")); + Serial.print(reader); + // Show some details of the PICC (that is: the tag/card) + Serial.print(F(": Card UID:")); + dump_byte_array(mfrc522[reader].uid.uidByte, mfrc522[reader].uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522[reader].PICC_GetType(mfrc522[reader].uid.sak); + Serial.println(mfrc522[reader].PICC_GetTypeName(piccType)); + + // Halt PICC + mfrc522[reader].PICC_HaltA(); + // Stop encryption on PCD + mfrc522[reader].PCD_StopCrypto1(); + } //if (mfrc522[reader].PICC_IsNewC + } //for(uint8_t reader +} + +/** + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} diff --git a/libraries/MFRC522/examples/firmware_check/firmware_check.ino b/libraries/MFRC522/examples/firmware_check/firmware_check.ino new file mode 100644 index 0000000..2213f3b --- /dev/null +++ b/libraries/MFRC522/examples/firmware_check/firmware_check.ino @@ -0,0 +1,63 @@ +/* + * -------------------------------------------------------------------------------------------------------------------- + * Example sketch/program to test your firmware. + * -------------------------------------------------------------------------------------------------------------------- + * This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid + * + * This example test the firmware of your MFRC522 reader module, only known version can be checked. If the test passed + * it do not mean that your module is faultless! Some modules have bad or broken antennas or the PICC is broken. + * + * @author Rotzbua + * @license Released into the public domain. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +/** + * Check firmware only once at startup + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 module + + Serial.println(F("*****************************")); + Serial.println(F("MFRC522 Digital self test")); + Serial.println(F("*****************************")); + mfrc522.PCD_DumpVersionToSerial(); // Show version of PCD - MFRC522 Card Reader + Serial.println(F("-----------------------------")); + Serial.println(F("Only known versions supported")); + Serial.println(F("-----------------------------")); + Serial.println(F("Performing test...")); + bool result = mfrc522.PCD_PerformSelfTest(); // perform the test + Serial.println(F("-----------------------------")); + Serial.print(F("Result: ")); + if (result) + Serial.println(F("OK")); + else + Serial.println(F("DEFECT or UNKNOWN")); + Serial.println(); +} + +void loop() {} // nothing to do diff --git a/libraries/MFRC522/examples/rfid_default_keys/rfid_default_keys.ino b/libraries/MFRC522/examples/rfid_default_keys/rfid_default_keys.ino new file mode 100644 index 0000000..13bce31 --- /dev/null +++ b/libraries/MFRC522/examples/rfid_default_keys/rfid_default_keys.ino @@ -0,0 +1,160 @@ +/* + * ---------------------------------------------------------------------------- + * This is a MFRC522 library example; see https://github.com/miguelbalboa/rfid + * for further details and other examples. + * + * NOTE: The library file MFRC522.h has a lot of useful info. Please read it. + * + * Released into the public domain. + * ---------------------------------------------------------------------------- + * Example sketch/program which will try the most used default keys listed in + * https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys to dump the + * block 0 of a MIFARE RFID card using a RFID-RC522 reader. + * + * Typical pin layout used: + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance. + +// Number of known default keys (hard-coded) +// NOTE: Synchronize the NR_KNOWN_KEYS define with the defaultKeys[] array +#define NR_KNOWN_KEYS 8 +// Known keys, see: https://code.google.com/p/mfcuk/wiki/MifareClassicDefaultKeys +byte knownKeys[NR_KNOWN_KEYS][MFRC522::MF_KEY_SIZE] = { + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, // FF FF FF FF FF FF = factory default + {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5}, // A0 A1 A2 A3 A4 A5 + {0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5}, // B0 B1 B2 B3 B4 B5 + {0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd}, // 4D 3A 99 C3 51 DD + {0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a}, // 1A 98 2C 7E 45 9A + {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}, // D3 F7 D3 F7 D3 F7 + {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, // AA BB CC DD EE FF + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // 00 00 00 00 00 00 +}; + +/* + * Initialize. + */ +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4) + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Try the most used default keys to print block 0 of a MIFARE PICC.")); +} + +/* + * Helper routine to dump a byte array as hex values to Serial. + */ +void dump_byte_array(byte *buffer, byte bufferSize) { + for (byte i = 0; i < bufferSize; i++) { + Serial.print(buffer[i] < 0x10 ? " 0" : " "); + Serial.print(buffer[i], HEX); + } +} + +/* + * Try using the PICC (the tag/card) with the given key to access block 0. + * On success, it will show the key details, and dump the block data on Serial. + * + * @return true when the given key worked, false otherwise. + */ +bool try_key(MFRC522::MIFARE_Key *key) +{ + bool result = false; + byte buffer[18]; + byte block = 0; + MFRC522::StatusCode status; + + // Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + // Serial.print(F("PCD_Authenticate() failed: ")); + // Serial.println(mfrc522.GetStatusCodeName(status)); + return false; + } + + // Read block + byte byteCount = sizeof(buffer); + status = mfrc522.MIFARE_Read(block, buffer, &byteCount); + if (status != MFRC522::STATUS_OK) { + // Serial.print(F("MIFARE_Read() failed: ")); + // Serial.println(mfrc522.GetStatusCodeName(status)); + } + else { + // Successful read + result = true; + Serial.print(F("Success with key:")); + dump_byte_array((*key).keyByte, MFRC522::MF_KEY_SIZE); + Serial.println(); + // Dump block data + Serial.print(F("Block ")); Serial.print(block); Serial.print(F(":")); + dump_byte_array(buffer, 16); + Serial.println(); + } + Serial.println(); + + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + return result; +} + +/* + * Main loop. + */ +void loop() { + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) + return; + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) + return; + + // Show some details of the PICC (that is: the tag/card) + Serial.print(F("Card UID:")); + dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size); + Serial.println(); + Serial.print(F("PICC type: ")); + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + // Try the known default keys + MFRC522::MIFARE_Key key; + for (byte k = 0; k < NR_KNOWN_KEYS; k++) { + // Copy the known key into the MIFARE_Key structure + for (byte i = 0; i < MFRC522::MF_KEY_SIZE; i++) { + key.keyByte[i] = knownKeys[k][i]; + } + // Try the key + if (try_key(&key)) { + // Found and reported on the key and block, + // no need to try other keys for this PICC + break; + } + + // http://arduino.stackexchange.com/a/14316 + if ( ! mfrc522.PICC_IsNewCardPresent()) + break; + if ( ! mfrc522.PICC_ReadCardSerial()) + break; + } +} diff --git a/libraries/MFRC522/examples/rfid_read_personal_data/rfid_read_personal_data.ino b/libraries/MFRC522/examples/rfid_read_personal_data/rfid_read_personal_data.ino new file mode 100644 index 0000000..993eda5 --- /dev/null +++ b/libraries/MFRC522/examples/rfid_read_personal_data/rfid_read_personal_data.ino @@ -0,0 +1,140 @@ +/* + * Initial Author: ryand1011 (https://github.com/ryand1011) + * + * Reads data written by a program such as "rfid_write_personal_data.ino" + * + * See: https://github.com/miguelbalboa/rfid/tree/master/examples/rfid_write_personal_data + * + * Uses MIFARE RFID card using RFID-RC522 reader + * Uses MFRC522 - Library + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout +*/ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +//*****************************************************************************************// +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Read personal data on a MIFARE PICC:")); //shows in serial that it is ready to read +} + +//*****************************************************************************************// +void loop() { + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + MFRC522::MIFARE_Key key; + for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; + + //some variables we need + byte block; + byte len; + MFRC522::StatusCode status; + + //------------------------------------------- + + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) { + return; + } + + Serial.println(F("**Card Detected:**")); + + //------------------------------------------- + + mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card + + //mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex + + //------------------------------------------- + + Serial.print(F("Name: ")); + + byte buffer1[18]; + + block = 4; + len = 18; + + //------------------------------------------- GET FIRST NAME + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 4, &key, &(mfrc522.uid)); //line 834 of MFRC522.cpp file + if (status != MFRC522::STATUS_OK) { + Serial.print(F("Authentication failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + status = mfrc522.MIFARE_Read(block, buffer1, &len); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("Reading failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + //PRINT FIRST NAME + for (uint8_t i = 0; i < 16; i++) + { + if (buffer1[i] != 32) + { + Serial.write(buffer1[i]); + } + } + Serial.print(" "); + + //---------------------------------------- GET LAST NAME + + byte buffer2[18]; + block = 1; + + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834 + if (status != MFRC522::STATUS_OK) { + Serial.print(F("Authentication failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + status = mfrc522.MIFARE_Read(block, buffer2, &len); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("Reading failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + //PRINT LAST NAME + for (uint8_t i = 0; i < 16; i++) { + Serial.write(buffer2[i] ); + } + + + //---------------------------------------- + + Serial.println(F("\n**End Reading**\n")); + + delay(1000); //change value if you want to read cards faster + + mfrc522.PICC_HaltA(); + mfrc522.PCD_StopCrypto1(); +} +//*****************************************************************************************// diff --git a/libraries/MFRC522/examples/rfid_write_personal_data/rfid_write_personal_data.ino b/libraries/MFRC522/examples/rfid_write_personal_data/rfid_write_personal_data.ino new file mode 100644 index 0000000..fa74670 --- /dev/null +++ b/libraries/MFRC522/examples/rfid_write_personal_data/rfid_write_personal_data.ino @@ -0,0 +1,158 @@ +/* + * Write personal data of a MIFARE RFID card using a RFID-RC522 reader + * Uses MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT. + * ----------------------------------------------------------------------------------------- + * MFRC522 Arduino Arduino Arduino Arduino Arduino + * Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro + * Signal Pin Pin Pin Pin Pin Pin + * ----------------------------------------------------------------------------------------- + * RST/Reset RST 9 5 D9 RESET/ICSP-5 RST + * SPI SS SDA(SS) 10 53 D10 10 10 + * SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16 + * SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14 + * SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15 + * + * More pin layouts for other boards can be found here: https://github.com/miguelbalboa/rfid#pin-layout + * + * Hardware required: + * Arduino + * PCD (Proximity Coupling Device): NXP MFRC522 Contactless Reader IC + * PICC (Proximity Integrated Circuit Card): A card or tag using the ISO 14443A interface, eg Mifare or NTAG203. + * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. + */ + +#include +#include + +#define RST_PIN 9 // Configurable, see typical pin layout above +#define SS_PIN 10 // Configurable, see typical pin layout above + +MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance + +void setup() { + Serial.begin(9600); // Initialize serial communications with the PC + SPI.begin(); // Init SPI bus + mfrc522.PCD_Init(); // Init MFRC522 card + Serial.println(F("Write personal data on a MIFARE PICC ")); +} + +void loop() { + + // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory. + MFRC522::MIFARE_Key key; + for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF; + + // Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle. + if ( ! mfrc522.PICC_IsNewCardPresent()) { + return; + } + + // Select one of the cards + if ( ! mfrc522.PICC_ReadCardSerial()) { + return; + } + + Serial.print(F("Card UID:")); //Dump UID + for (byte i = 0; i < mfrc522.uid.size; i++) { + Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); + Serial.print(mfrc522.uid.uidByte[i], HEX); + } + Serial.print(F(" PICC type: ")); // Dump PICC type + MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); + Serial.println(mfrc522.PICC_GetTypeName(piccType)); + + byte buffer[34]; + byte block; + MFRC522::StatusCode status; + byte len; + + Serial.setTimeout(20000L) ; // wait until 20 seconds for input from serial + // Ask personal data: Family name + Serial.println(F("Type Family name, ending with #")); + len = Serial.readBytesUntil('#', (char *) buffer, 30) ; // read family name from serial + for (byte i = len; i < 30; i++) buffer[i] = ' '; // pad with spaces + + block = 1; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("PCD_Authenticate() success: ")); + + // Write block + status = mfrc522.MIFARE_Write(block, buffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + block = 2; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, &buffer[16], 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + // Ask personal data: First name + Serial.println(F("Type First name, ending with #")); + len = Serial.readBytesUntil('#', (char *) buffer, 20) ; // read first name from serial + for (byte i = len; i < 20; i++) buffer[i] = ' '; // pad with spaces + + block = 4; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, buffer, 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + block = 5; + //Serial.println(F("Authenticating using key A...")); + status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, block, &key, &(mfrc522.uid)); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + + // Write block + status = mfrc522.MIFARE_Write(block, &buffer[16], 16); + if (status != MFRC522::STATUS_OK) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(mfrc522.GetStatusCodeName(status)); + return; + } + else Serial.println(F("MIFARE_Write() success: ")); + + + Serial.println(" "); + mfrc522.PICC_HaltA(); // Halt PICC + mfrc522.PCD_StopCrypto1(); // Stop encryption on PCD + +} diff --git a/libraries/MFRC522/keywords.txt b/libraries/MFRC522/keywords.txt new file mode 100644 index 0000000..df83920 --- /dev/null +++ b/libraries/MFRC522/keywords.txt @@ -0,0 +1,223 @@ +####################################### +# Syntax Coloring Map for library MFRC522 +####################################### + +####################################### +# KEYWORD1 Classes, datatypes, and C++ keywords +####################################### +MFRC522 KEYWORD1 +MFRC522Extended KEYWORD1 +PCD_Register KEYWORD1 +PCD_Command KEYWORD1 +PCD_RxGain KEYWORD1 +PICC_Command KEYWORD1 +MIFARE_Misc KEYWORD1 +PICC_Type KEYWORD1 +StatusCode KEYWORD1 +TagBitRates KEYWORD1 +Uid KEYWORD1 +CardInfo KEYWORD1 +MIFARE_Key KEYWORD1 +PcbBlock KEYWORD1 + +####################################### +# KEYWORD2 Methods and functions +####################################### + +# Basic interface functions for communicating with the MFRC522 +PCD_WriteRegister KEYWORD2 +PCD_WriteRegister KEYWORD2 +PCD_ReadRegister KEYWORD2 +PCD_ReadRegister KEYWORD2 +setBitMask KEYWORD2 +PCD_SetRegisterBitMask KEYWORD2 +PCD_ClearRegisterBitMask KEYWORD2 +PCD_CalculateCRC KEYWORD2 + +# Functions for manipulating the MFRC522 +PCD_Init KEYWORD2 +PCD_Reset KEYWORD2 +PCD_AntennaOn KEYWORD2 +PCD_AntennaOff KEYWORD2 +PCD_GetAntennaGain KEYWORD2 +PCD_SetAntennaGain KEYWORD2 +PCD_PerformSelfTest KEYWORD2 + +# Power control functions MFRC522 +PCD_SoftPowerDown KEYWORD2 +PCD_SoftPowerUp KEYWORD2 + +# Functions for communicating with PICCs +PCD_TransceiveData KEYWORD2 +PCD_CommunicateWithPICC KEYWORD2 +PICC_RequestA KEYWORD2 +PICC_WakeupA KEYWORD2 +PICC_REQA_or_WUPA KEYWORD2 +PICC_Select KEYWORD2 +PICC_HaltA KEYWORD2 +PICC_RATS KEYWORD2 +PICC_PPS KEYWORD2 + +# Functions for communicating with ISO/IEC 14433-4 cards +TCL_Transceive KEYWORD2 +TCL_TransceiveRBlock KEYWORD2 +TCL_Deselect KEYWORD2 + +# Functions for communicating with MIFARE PICCs +PCD_Authenticate KEYWORD2 +PCD_StopCrypto1 KEYWORD2 +MIFARE_Read KEYWORD2 +MIFARE_Write KEYWORD2 +MIFARE_Increment KEYWORD2 +MIFARE_Ultralight_Write KEYWORD2 +MIFARE_GetValue KEYWORD2 +MIFARE_SetValue KEYWORD2 +PCD_NTAG216_AUTH KEYWORD2 + +# Support functions +PCD_MIFARE_Transceive KEYWORD2 +GetStatusCodeName KEYWORD2 +PICC_GetType KEYWORD2 +PICC_GetTypeName KEYWORD2 + +# Support functions for debuging +PCD_DumpVersionToSerial KEYWORD2 +PICC_DumpToSerial KEYWORD2 +PICC_DumpDetailsToSerial KEYWORD2 +PICC_DumpMifareClassicToSerial KEYWORD2 +PICC_DumpMifareClassicSectorToSerial KEYWORD2 +PICC_DumpMifareUltralightToSerial KEYWORD2 +PICC_DumpISO14443_4 KEYWORD2 + +# Advanced functions for MIFARE +MIFARE_SetAccessBits KEYWORD2 +MIFARE_OpenUidBackdoor KEYWORD2 +MIFARE_SetUid KEYWORD2 +MIFARE_UnbrickUidSector KEYWORD2 + +# Convenience functions - does not add extra functionality +PICC_IsNewCardPresent KEYWORD2 +PICC_ReadCardSerial KEYWORD2 + +####################################### +# KEYWORD3 setup and loop functions, as well as the Serial keywords +####################################### + +####################################### +# LITERAL1 Constants +####################################### +CommandReg LITERAL1 +ComIEnReg LITERAL1 +DivIEnReg LITERAL1 +ComIrqReg LITERAL1 +DivIrqReg LITERAL1 +ErrorReg LITERAL1 +Status1Reg LITERAL1 +Status2Reg LITERAL1 +FIFODataReg LITERAL1 +FIFOLevelReg LITERAL1 +WaterLevelReg LITERAL1 +ControlReg LITERAL1 +BitFramingReg LITERAL1 +CollReg LITERAL1 +ModeReg LITERAL1 +TxModeReg LITERAL1 +RxModeReg LITERAL1 +TxControlReg LITERAL1 +TxASKReg LITERAL1 +TxSelReg LITERAL1 +RxSelReg LITERAL1 +RxThresholdReg LITERAL1 +DemodReg LITERAL1 +MfTxReg LITERAL1 +MfRxReg LITERAL1 +SerialSpeedReg LITERAL1 +CRCResultRegH LITERAL1 +CRCResultRegL LITERAL1 +ModWidthReg LITERAL1 +RFCfgReg LITERAL1 +GsNReg LITERAL1 +CWGsPReg LITERAL1 +ModGsPReg LITERAL1 +TModeReg LITERAL1 +TPrescalerReg LITERAL1 +TReloadRegH LITERAL1 +TReloadRegL LITERAL1 +TCounterValueRegH LITERAL1 +TCounterValueRegL LITERAL1 +TestSel1Reg LITERAL1 +TestSel2Reg LITERAL1 +TestPinEnReg LITERAL1 +TestPinValueReg LITERAL1 +TestBusReg LITERAL1 +AutoTestReg LITERAL1 +VersionReg LITERAL1 +AnalogTestReg LITERAL1 +TestDAC1Reg LITERAL1 +TestDAC2Reg LITERAL1 +TestADCReg LITERAL1 +PCD_Idle LITERAL1 +PCD_Mem LITERAL1 +PCD_GenerateRandomID LITERAL1 +PCD_CalcCRC LITERAL1 +PCD_Transmit LITERAL1 +PCD_NoCmdChange LITERAL1 +PCD_Receive LITERAL1 +PCD_Transceive LITERAL1 +PCD_MFAuthent LITERAL1 +PCD_SoftReset LITERAL1 +RxGain_18dB LITERAL1 +RxGain_23dB LITERAL1 +RxGain_18dB_2 LITERAL1 +RxGain_23dB_2 LITERAL1 +RxGain_33dB LITERAL1 +RxGain_38dB LITERAL1 +RxGain_43dB LITERAL1 +RxGain_48dB LITERAL1 +RxGain_min LITERAL1 +RxGain_avg LITERAL1 +RxGain_max LITERAL1 +PICC_CMD_REQA LITERAL1 +PICC_CMD_WUPA LITERAL1 +PICC_CMD_CT LITERAL1 +PICC_CMD_SEL_CL1 LITERAL1 +PICC_CMD_SEL_CL2 LITERAL1 +PICC_CMD_SEL_CL3 LITERAL1 +PICC_CMD_HLTA LITERAL1 +PICC_CMD_RATS LITERAL1 +PICC_CMD_MF_AUTH_KEY_A LITERAL1 +PICC_CMD_MF_AUTH_KEY_B LITERAL1 +PICC_CMD_MF_READ LITERAL1 +PICC_CMD_MF_WRITE LITERAL1 +PICC_CMD_MF_DECREMENT LITERAL1 +PICC_CMD_MF_INCREMENT LITERAL1 +PICC_CMD_MF_RESTORE LITERAL1 +PICC_CMD_MF_TRANSFER LITERAL1 +PICC_CMD_UL_WRITE LITERAL1 +MF_ACK LITERAL1 +MF_KEY_SIZE LITERAL1 +PICC_TYPE_UNKNOWN LITERAL1 +PICC_TYPE_ISO_14443_4 LITERAL1 +PICC_TYPE_ISO_18092 LITERAL1 +PICC_TYPE_MIFARE_MINI LITERAL1 +PICC_TYPE_MIFARE_1K LITERAL1 +PICC_TYPE_MIFARE_4K LITERAL1 +PICC_TYPE_MIFARE_UL LITERAL1 +PICC_TYPE_MIFARE_PLUS LITERAL1 +PICC_TYPE_MIFARE_DESFIRE LITERAL1 +PICC_TYPE_TNP3XXX LITERAL1 +PICC_TYPE_NOT_COMPLETE LITERAL1 +STATUS_OK LITERAL1 +STATUS_ERROR LITERAL1 +STATUS_COLLISION LITERAL1 +STATUS_TIMEOUT LITERAL1 +STATUS_NO_ROOM LITERAL1 +STATUS_INTERNAL_ERROR LITERAL1 +STATUS_INVALID LITERAL1 +STATUS_CRC_WRONG LITERAL1 +STATUS_MIFARE_NACK LITERAL1 +FIFO_SIZE LITERAL1 +BITRATE_106KBITS LITERAL1 +BITRATE_212KBITS LITERAL1 +BITRATE_424KBITS LITERAL1 +BITRATE_848KBITS LITERAL1 diff --git a/libraries/MFRC522/library.json b/libraries/MFRC522/library.json new file mode 100644 index 0000000..9cd0e27 --- /dev/null +++ b/libraries/MFRC522/library.json @@ -0,0 +1,15 @@ +{ + "name": "MFRC522", + "version": "1.4.11", + "license": "Unlicense", + "keywords": "rfid, spi", + "description": "Arduino RFID Library for MFRC522 (SPI). Read/Write a RFID Card or Tag using the ISO/IEC 14443A/MIFARE interface.", + "repository": + { + "type": "git", + "url": "https://github.com/miguelbalboa/rfid.git" + }, + "exclude": "doc", + "frameworks": "arduino", + "platforms": ["atmelavr", "atmelsam", "ststm32", "espressif8266", "espressif32", "samd"] +} diff --git a/libraries/MFRC522/library.properties b/libraries/MFRC522/library.properties new file mode 100644 index 0000000..949bdc8 --- /dev/null +++ b/libraries/MFRC522/library.properties @@ -0,0 +1,9 @@ +name=MFRC522 +version=1.4.11 +author=GithubCommunity +maintainer=GithubCommunity +sentence=Arduino RFID Library for MFRC522 (SPI) +paragraph=Read/Write a RFID Card or Tag using the ISO/IEC 14443A/MIFARE interface. +category=Communication +url=https://github.com/miguelbalboa/rfid +architectures=avr,megaavr,STM32F1,teensy,esp8266,esp32,samd,atmelsam,MIK32_Amur diff --git a/libraries/MFRC522/src/MFRC522.cpp b/libraries/MFRC522/src/MFRC522.cpp new file mode 100644 index 0000000..7da85c1 --- /dev/null +++ b/libraries/MFRC522/src/MFRC522.cpp @@ -0,0 +1,1950 @@ +/* +* 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 +#include "MFRC522.h" + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for setting up the Arduino +///////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +MFRC522::MFRC522(): MFRC522(SS, UINT8_MAX) { // SS is defined in pins_arduino.h, UINT8_MAX means there is no connection from Arduino to MFRC522's reset and power down input +} // End constructor + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522( byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). + ): MFRC522(SS, resetPowerDownPin) { // SS is defined in pins_arduino.h +} // End constructor + +/** + * Constructor. + * Prepares the output pins. + */ +MFRC522::MFRC522( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low). If there is no connection from the CPU to NRSTPD, set this to UINT8_MAX. In this case, only soft reset will be used in PCD_Init(). + ) { + _chipSelectPin = chipSelectPin; + _resetPowerDownPin = resetPowerDownPin; +} // End constructor + +///////////////////////////////////////////////////////////////////////////////////// +// 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( PCD_Register reg, ///< The register to write to. One of the PCD_Register enums. + byte value ///< The value to write. + ) { + SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + SPI.transfer(value); + digitalWrite(_chipSelectPin, HIGH); // Release slave again + SPI.endTransaction(); // Stop using the SPI bus +} // 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( PCD_Register 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. + ) { + SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(reg); // MSB == 0 is for writing. LSB is not used in address. Datasheet section 8.1.2.3. + for (byte index = 0; index < count; index++) { + SPI.transfer(values[index]); + } + digitalWrite(_chipSelectPin, HIGH); // Release slave again + SPI.endTransaction(); // Stop using the SPI bus +} // 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( PCD_Register reg ///< The register to read from. One of the PCD_Register enums. + ) { + byte value; + SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + digitalWrite(_chipSelectPin, LOW); // Select slave + SPI.transfer(0x80 | reg); // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + value = SPI.transfer(0); // Read the value back. Send 0 to stop reading. + digitalWrite(_chipSelectPin, HIGH); // Release slave again + SPI.endTransaction(); // Stop using the SPI bus + return value; +} // End PCD_ReadRegister() + +/** + * Reads a number of bytes from the specified register in the MFRC522 chip. + * The interface is described in the datasheet section 8.1.2. + */ +void MFRC522::PCD_ReadRegister( PCD_Register 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; // MSB == 1 is for reading. LSB is not used in address. Datasheet section 8.1.2.3. + byte index = 0; // Index in values array. + SPI.beginTransaction(SPISettings(MFRC522_SPICLOCK, MSBFIRST, SPI_MODE0)); // Set the settings to work with SPI bus + digitalWrite(_chipSelectPin, LOW); // Select slave + count--; // One read is performed outside of the loop + SPI.transfer(address); // Tell MFRC522 which address we want to read + if (rxAlign) { // Only update bit positions rxAlign..7 in values[0] + // Create bit mask for bit positions rxAlign..7 + byte mask = (0xFF << rxAlign) & 0xFF; + // Read value and tell that we want to read the same address again. + byte value = SPI.transfer(address); + // Apply mask to both current value of values[0] and the new data in value. + values[0] = (values[0] & ~mask) | (value & mask); + index++; + } + while (index < count) { + values[index] = SPI.transfer(address); // Read value and tell that we want to read the same address again. + index++; + } + values[index] = SPI.transfer(0); // Read the final byte. Send 0 to stop reading. + digitalWrite(_chipSelectPin, HIGH); // Release slave again + SPI.endTransaction(); // Stop using the SPI bus +} // End PCD_ReadRegister() + +/** + * Sets the bits given in mask in register reg. + */ +void MFRC522::PCD_SetRegisterBitMask( PCD_Register 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( PCD_Register 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. + */ +MFRC522::StatusCode 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_WriteRegister(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. Check for the register to + // indicate that the CRC calculation is complete in a loop. If the + // calculation is not indicated as complete in ~90ms, then time out + // the operation. + const uint32_t deadline = millis() + 89; + + do { + // DivIrqReg[7..0] bits are: Set2 reserved reserved MfinActIRq reserved CRCIRq reserved reserved + byte n = PCD_ReadRegister(DivIrqReg); + if (n & 0x04) { // CRCIRq bit set - calculation done + 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; + } + yield(); + } + while (static_cast (millis()) < deadline); + + // 89ms passed and nothing happened. Communication with the MFRC522 might be down. + return STATUS_TIMEOUT; +} // End PCD_CalculateCRC() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for manipulating the MFRC522 +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init() { + bool hardReset = false; + + // Set the chipSelectPin as digital output, do not select the slave yet + pinMode(_chipSelectPin, OUTPUT); + digitalWrite(_chipSelectPin, HIGH); + + // If a valid pin number has been set, pull device out of power down / reset state. + if (_resetPowerDownPin != UNUSED_PIN) { + // First set the resetPowerDownPin as digital input, to check the MFRC522 power down mode. + pinMode(_resetPowerDownPin, INPUT); + + if (digitalRead(_resetPowerDownPin) == LOW) { // The MFRC522 chip is in power down mode. + pinMode(_resetPowerDownPin, OUTPUT); // Now set the resetPowerDownPin as digital output. + digitalWrite(_resetPowerDownPin, LOW); // Make sure we have a clean LOW state. + delayMicroseconds(2); // 8.8.1 Reset timing requirements says about 100ns. Let us be generous: 2μsl + digitalWrite(_resetPowerDownPin, HIGH); // Exit power down mode. This triggers a hard reset. + // Section 8.8.2 in the datasheet says the oscillator start-up time is the start up time of the crystal + 37,74μs. Let us be generous: 50ms. + delay(50); + hardReset = true; + } + } + + if (!hardReset) { // Perform a soft reset if we haven't triggered a hard reset above. + PCD_Reset(); + } + + // Reset baud rates + PCD_WriteRegister(TxModeReg, 0x00); + PCD_WriteRegister(RxModeReg, 0x00); + // Reset ModWidthReg + PCD_WriteRegister(ModWidthReg, 0x26); + + // 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() + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init( byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + PCD_Init(SS, resetPowerDownPin); // SS is defined in pins_arduino.h +} // End PCD_Init() + +/** + * Initializes the MFRC522 chip. + */ +void MFRC522::PCD_Init( byte chipSelectPin, ///< Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte resetPowerDownPin ///< Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + ) { + _chipSelectPin = chipSelectPin; + _resetPowerDownPin = resetPowerDownPin; + // Set the chipSelectPin as digital output, do not select the slave yet + PCD_Init(); +} // 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. + uint8_t count = 0; + do { + // Wait for the PowerDown bit in CommandReg to be cleared (max 3x50ms) + delay(50); + } while ((PCD_ReadRegister(CommandReg) & (1 << 4)) && (++count) < 3); +} // 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. Or false if no firmware reference is available. + */ +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_WriteRegister(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 + byte n; + for (uint8_t i = 0; i < 0xFF; i++) { + // The datasheet does not specify exact completion condition except + // that FIFO buffer should contain 64 bytes. + // While selftest is initiated by CalcCRC command + // it behaves differently from normal CRC computation, + // so one can't reliably use DivIrqReg to check for completion. + // It is reported that some devices does not trigger CRCIRq flag + // during selftest. + n = PCD_ReadRegister(FIFOLevelReg); + if (n >= 64) { + 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 0x88: // Fudan Semiconductor FM17522 clone + reference = FM17522_firmware_reference; + break; + case 0x90: // Version 0.0 + reference = MFRC522_firmware_referenceV0_0; + break; + 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; // abort test + } + + // Verify that the results match up to our expectations + for (uint8_t i = 0; i < 64; i++) { + if (result[i] != pgm_read_byte(&(reference[i]))) { + return false; + } + } + + // 8. Perform a re-init, because PCD does not work after test. + // Reset does not work as expected. + // "Auto self-test done" does not work as expected. + PCD_Init(); + + // Test passed; all is good. + return true; +} // End PCD_PerformSelfTest() + +///////////////////////////////////////////////////////////////////////////////////// +// Power control +///////////////////////////////////////////////////////////////////////////////////// + +//IMPORTANT NOTE!!!! +//Calling any other function that uses CommandReg will disable soft power down mode !!! +//For more details about power control, refer to the datasheet - page 33 (8.6) + +void MFRC522::PCD_SoftPowerDown(){//Note : Only soft power down mode is available throught software + byte val = PCD_ReadRegister(CommandReg); // Read state of the command register + val |= (1<<4);// set PowerDown bit ( bit 4 ) to 1 + PCD_WriteRegister(CommandReg, val);//write new value to the command register +} + +void MFRC522::PCD_SoftPowerUp(){ + byte val = PCD_ReadRegister(CommandReg); // Read state of the command register + val &= ~(1<<4);// set PowerDown bit ( bit 4 ) to 0 + PCD_WriteRegister(CommandReg, val);//write new value to the command register + // wait until PowerDown bit is cleared (this indicates end of wake up procedure) + const uint32_t timeout = (uint32_t)millis() + 500;// create timer for timeout (just in case) + + while(millis()<=timeout){ // set timeout to 500 ms + val = PCD_ReadRegister(CommandReg);// Read state of the command register + if(!(val & (1<<4))){ // if powerdown bit is 0 + break;// wake up procedure is finished + } + yield(); + } +} + +///////////////////////////////////////////////////////////////////////////////////// +// 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. + */ +MFRC522::StatusCode 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, ///< nullptr 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 nullptr. + 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. + */ +MFRC522::StatusCode 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, ///< nullptr 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. + ) { + // 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_WriteRegister(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 + } + + // In PCD_Init() we set the TAuto flag in TModeReg. This means the timer + // automatically starts when the PCD stops transmitting. + // + // Wait here for the command to complete. The bits specified in the + // `waitIRq` parameter define what bits constitute a completed command. + // When they are set in the ComIrqReg register, then the command is + // considered complete. If the command is not indicated as complete in + // ~36ms, then consider the command as timed out. + const uint32_t deadline = millis() + 36; + bool completed = false; + + do { + byte 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. + completed = true; + break; + } + if (n & 0x01) { // Timer interrupt - nothing received in 25ms + return STATUS_TIMEOUT; + } + yield(); + } + while (static_cast (millis()) < deadline); + + // 36ms and nothing happened. Communication with the MFRC522 might be down. + if (!completed) { + 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; + } + + byte _validBits = 0; + + // If the caller wants data back, get it from the MFRC522. + if (backData && backLen) { + byte 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]; + MFRC522::StatusCode status = PCD_CalculateCRC(&backData[0], *backLen - 2, &controlBuffer[0]); + if (status != STATUS_OK) { + return status; + } + 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. + */ +MFRC522::StatusCode 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. + */ +MFRC522::StatusCode 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. + */ +MFRC522::StatusCode 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; + MFRC522::StatusCode status; + + if (bufferATQA == nullptr || *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. + */ +MFRC522::StatusCode 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; + MFRC522::StatusCode result; + byte count; + byte checkBit; + byte index; + byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + 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 are 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 separate 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. + byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (valueOfCollReg & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + byte collisionPos = valueOfCollReg & 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 % 8; // The bit to modify + checkBit = (currentLevelKnownBits - 1) % 8; + index = 1 + (currentLevelKnownBits / 8) + (count ? 1 : 0); // First byte is index 0. + buffer[index] |= (1 << checkBit); + } + else if (result != 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. + */ +MFRC522::StatusCode MFRC522::PICC_HaltA() { + MFRC522::StatusCode 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 a success. + result = PCD_TransceiveData(buffer, sizeof(buffer), nullptr, 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. + */ +MFRC522::StatusCode 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]; + } + // Use the last uid bytes as specified in http://cache.nxp.com/documents/application_note/AN10927.pdf + // section 3.2.5 "MIFARE Classic Authentication". + // The only missed case is the MF1Sxxxx shortcut activation, + // but it requires cascade tag (CT) byte, that is not part of uid. + for (byte i = 0; i < 4; i++) { // The last 4 bytes of the UID + sendData[8+i] = uid->uidByte[i+uid->size-4]; + } + + // 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. + */ +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode result; + + // Sanity check + if (buffer == nullptr || *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, nullptr, 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. + */ +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode result; + + // Sanity check + if (buffer == nullptr || 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. + */ +MFRC522::StatusCode 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. + ) { + MFRC522::StatusCode result; + + // Sanity check + if (buffer == nullptr || 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_Decrement( byte blockAddr, ///< The block (0-0xff) number. + int32_t 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_Increment( byte blockAddr, ///< The block (0-0xff) number. + int32_t 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. + */ +MFRC522::StatusCode 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_TwoStepHelper( byte command, ///< The command to use + byte blockAddr, ///< The block (0-0xff) number. + int32_t data ///< The data to transfer in step 2 + ) { + MFRC522::StatusCode 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_Transfer( byte blockAddr ///< The block (0-0xff) number. + ) { + MFRC522::StatusCode 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. + */ +MFRC522::StatusCode MFRC522::MIFARE_GetValue(byte blockAddr, int32_t *value) { + MFRC522::StatusCode 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 = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(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. + */ +MFRC522::StatusCode MFRC522::MIFARE_SetValue(byte blockAddr, int32_t value) { + byte buffer[18]; + + // Translate the int32_t 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() + +/** + * Authenticate with a NTAG216. + * + * Only for NTAG216. First implemented by Gargantuanman. + * + * @param[in] passWord password. + * @param[in] pACK result success???. + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522::PCD_NTAG216_AUTH(byte* passWord, byte pACK[]) //Authenticate with 32bit password +{ + // TODO: Fix cmdBuffer length and rxlength. They really should match. + // (Better still, rxlength should not even be necessary.) + + MFRC522::StatusCode result; + byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + cmdBuffer[0] = 0x1B; //Comando de autentificacion + + for (byte i = 0; i<4; i++) + cmdBuffer[i+1] = passWord[i]; + + result = PCD_CalculateCRC(cmdBuffer, 5, &cmdBuffer[5]); + + if (result!=STATUS_OK) { + return result; + } + + // Transceive the data, store the reply in cmdBuffer[] + byte waitIRq = 0x30; // RxIRq and IdleIRq +// byte cmdBufferSize = sizeof(cmdBuffer); + byte validBits = 0; + byte rxlength = 5; + result = PCD_CommunicateWithPICC(PCD_Transceive, waitIRq, cmdBuffer, 7, cmdBuffer, &rxlength, &validBits); + + pACK[0] = cmdBuffer[0]; + pACK[1] = cmdBuffer[1]; + + if (result!=STATUS_OK) { + return result; + } + + return STATUS_OK; +} // End PCD_NTAG216_AUTH() + + +///////////////////////////////////////////////////////////////////////////////////// +// 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. + */ +MFRC522::StatusCode 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 + ) { + MFRC522::StatusCode result; + byte cmdBuffer[18]; // We need room for 16 bytes data and 2 bytes CRC_A. + + // Sanity check + if (sendData == nullptr || 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. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::GetStatusCodeName(MFRC522::StatusCode code ///< One of the StatusCode enums. + ) { + switch (code) { + case STATUS_OK: return F("Success."); + case STATUS_ERROR: return F("Error in communication."); + case STATUS_COLLISION: return F("Collision detected."); + case STATUS_TIMEOUT: return F("Timeout in communication."); + case STATUS_NO_ROOM: return F("A buffer is not big enough."); + case STATUS_INTERNAL_ERROR: return F("Internal error in the code. Should not happen."); + case STATUS_INVALID: return F("Invalid argument."); + case STATUS_CRC_WRONG: return F("The CRC_A does not match."); + case STATUS_MIFARE_NACK: return F("A MIFARE PICC responded with NAK."); + default: return F("Unknown error"); + } +} // End GetStatusCodeName() + +/** + * Translates the SAK (Select Acknowledge) to a PICC type. + * + * @return PICC_Type + */ +MFRC522::PICC_Type MFRC522::PICC_GetType(byte sak ///< The SAK byte returned from PICC_Select(). + ) { + // http://www.nxp.com/documents/application_note/AN10833.pdf + // 3.2 Coding of Select Acknowledge (SAK) + // ignore 8-bit (iso14443 starts with LSBit = bit 1) + // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) + sak &= 0x7F; + switch (sak) { + case 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete + case 0x09: return PICC_TYPE_MIFARE_MINI; + case 0x08: return PICC_TYPE_MIFARE_1K; + case 0x18: return PICC_TYPE_MIFARE_4K; + case 0x00: return PICC_TYPE_MIFARE_UL; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; + case 0x01: return PICC_TYPE_TNP3XXX; + case 0x20: return PICC_TYPE_ISO_14443_4; + case 0x40: return PICC_TYPE_ISO_18092; + default: return PICC_TYPE_UNKNOWN; + } +} // End PICC_GetType() + +/** + * Returns a __FlashStringHelper pointer to the PICC type name. + * + * @return const __FlashStringHelper * + */ +const __FlashStringHelper *MFRC522::PICC_GetTypeName(PICC_Type piccType ///< One of the PICC_Type enums. + ) { + switch (piccType) { + case PICC_TYPE_ISO_14443_4: return F("PICC compliant with ISO/IEC 14443-4"); + case PICC_TYPE_ISO_18092: return F("PICC compliant with ISO/IEC 18092 (NFC)"); + case PICC_TYPE_MIFARE_MINI: return F("MIFARE Mini, 320 bytes"); + case PICC_TYPE_MIFARE_1K: return F("MIFARE 1KB"); + case PICC_TYPE_MIFARE_4K: return F("MIFARE 4KB"); + case PICC_TYPE_MIFARE_UL: return F("MIFARE Ultralight or Ultralight C"); + case PICC_TYPE_MIFARE_PLUS: return F("MIFARE Plus"); + case PICC_TYPE_MIFARE_DESFIRE: return F("MIFARE DESFire"); + case PICC_TYPE_TNP3XXX: return F("MIFARE TNP3XXX"); + case PICC_TYPE_NOT_COMPLETE: return F("SAK indicates UID is not complete."); + case PICC_TYPE_UNKNOWN: + default: return F("Unknown type"); + } +} // End PICC_GetTypeName() + +/** + * Dumps debug info about the connected PCD to Serial. + * Shows all known firmware versions + */ +void MFRC522::PCD_DumpVersionToSerial() { + // Get the MFRC522 firmware version + byte v = PCD_ReadRegister(VersionReg); + Serial.print(F("Firmware Version: 0x")); + Serial.print(v, HEX); + // Lookup which version + switch(v) { + case 0x88: Serial.println(F(" = (clone)")); break; + case 0x90: Serial.println(F(" = v0.0")); break; + case 0x91: Serial.println(F(" = v1.0")); break; + case 0x92: Serial.println(F(" = v2.0")); break; + case 0x12: Serial.println(F(" = counterfeit chip")); break; + default: Serial.println(F(" = (unknown)")); + } + // When 0x00 or 0xFF is returned, communication probably failed + if ((v == 0x00) || (v == 0xFF)) + Serial.println(F("WARNING: Communication failure, is the MFRC522 properly connected?")); +} // End PCD_DumpVersionToSerial() + +/** + * 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; + + // Dump UID, SAK and Type + PICC_DumpDetailsToSerial(uid); + + // Dump contents + PICC_Type piccType = PICC_GetType(uid->sak); + 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_MIFARE_DESFIRE: + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} // End PICC_DumpToSerial() + +/** + * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. + */ +void MFRC522::PICC_DumpDetailsToSerial(Uid *uid ///< Pointer to Uid struct returned from a successful PICC_Select(). + ) { + // UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < uid->size; i++) { + if(uid->uidByte[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(uid->uidByte[i], HEX); + } + Serial.println(); + + // SAK + Serial.print(F("Card SAK: ")); + if(uid->sak < 0x10) + Serial.print(F("0")); + Serial.println(uid->sak, HEX); + + // (suggested) PICC type + PICC_Type piccType = PICC_GetType(uid->sak); + Serial.print(F("PICC type: ")); + Serial.println(PICC_GetTypeName(piccType)); +} // End PICC_DumpDetailsToSerial() + +/** + * 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(). + PICC_Type piccType, ///< One of the PICC_Type enums. + MIFARE_Key *key ///< Key A used for all sectors. + ) { + byte no_of_sectors = 0; + switch (piccType) { + case PICC_TYPE_MIFARE_MINI: + // Has 5 sectors * 4 blocks/sector * 16 bytes/block = 320 bytes. + no_of_sectors = 5; + break; + + case PICC_TYPE_MIFARE_1K: + // Has 16 sectors * 4 blocks/sector * 16 bytes/block = 1024 bytes. + no_of_sectors = 16; + break; + + case PICC_TYPE_MIFARE_4K: + // Has (32 sectors * 4 blocks/sector + 8 sectors * 16 blocks/sector) * 16 bytes/block = 4096 bytes. + no_of_sectors = 40; + break; + + default: // Should not happen. Ignore. + break; + } + + // Dump sectors, highest address first. + if (no_of_sectors) { + Serial.println(F("Sector Block 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 AccessBits")); + for (int8_t 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. + ) { + MFRC522::StatusCode 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; + invertedError = false; // Avoid "unused variable" warning. + for (int8_t blockOffset = no_of_blocks - 1; blockOffset >= 0; blockOffset--) { + blockAddr = firstBlock + blockOffset; + // Sector number - only on first line + if (isSectorTrailer) { + if(sector < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(sector); + Serial.print(F(" ")); + } + else { + Serial.print(F(" ")); + } + // Block number + if(blockAddr < 10) + Serial.print(F(" ")); // Pad with spaces + else { + if(blockAddr < 100) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + } + Serial.print(blockAddr); + Serial.print(F(" ")); + // Establish encrypted communications before reading the first block + if (isSectorTrailer) { + status = PCD_Authenticate(PICC_CMD_MF_AUTH_KEY_A, firstBlock, key, uid); + if (status != STATUS_OK) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + return; + } + } + // Read block + byteCount = sizeof(buffer); + status = MIFARE_Read(blockAddr, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + continue; + } + // Dump data + for (byte index = 0; index < 16; index++) { + if(buffer[index] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[index], HEX); + if ((index % 4) == 3) { + Serial.print(F(" ")); + } + } + // Parse sector trailer data + if (isSectorTrailer) { + c1 = buffer[7] >> 4; + c2 = buffer[8] & 0xF; + c3 = buffer[8] >> 4; + c1_ = buffer[6] & 0xF; + c2_ = buffer[6] >> 4; + c3_ = buffer[7] & 0xF; + invertedError = (c1 != (~c1_ & 0xF)) || (c2 != (~c2_ & 0xF)) || (c3 != (~c3_ & 0xF)); + g[0] = ((c1 & 1) << 2) | ((c2 & 1) << 1) | ((c3 & 1) << 0); + g[1] = ((c1 & 2) << 1) | ((c2 & 2) << 0) | ((c3 & 2) >> 1); + g[2] = ((c1 & 4) << 0) | ((c2 & 4) >> 1) | ((c3 & 4) >> 2); + g[3] = ((c1 & 8) >> 1) | ((c2 & 8) >> 2) | ((c3 & 8) >> 3); + isSectorTrailer = false; + } + + // Which access group is this block in? + if (no_of_blocks == 4) { + group = blockOffset; + firstInGroup = true; + } + else { + group = blockOffset / 5; + firstInGroup = (group == 3) || (group != (blockOffset + 1) / 5); + } + + if (firstInGroup) { + // Print access bits + Serial.print(F(" [ ")); + Serial.print((g[group] >> 2) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 1) & 1, DEC); Serial.print(F(" ")); + Serial.print((g[group] >> 0) & 1, DEC); + Serial.print(F(" ] ")); + if (invertedError) { + Serial.print(F(" Inverted access bits did not match! ")); + } + } + + if (group != 3 && (g[group] == 1 || g[group] == 6)) { // Not a sector trailer, a value block + int32_t value = (int32_t(buffer[3])<<24) | (int32_t(buffer[2])<<16) | (int32_t(buffer[1])<<8) | int32_t(buffer[0]); + Serial.print(F(" Value=0x")); Serial.print(value, HEX); + Serial.print(F(" Adr=0x")); Serial.print(buffer[12], HEX); + } + Serial.println(); + } + + return; +} // End PICC_DumpMifareClassicSectorToSerial() + +/** + * Dumps memory contents of a MIFARE Ultralight PICC. + */ +void MFRC522::PICC_DumpMifareUltralightToSerial() { + MFRC522::StatusCode status; + byte byteCount; + byte buffer[18]; + byte i; + + Serial.println(F("Page 0 1 2 3")); + // Try the mpages of the original Ultralight. Ultralight C has more pages. + for (byte page = 0; page < 16; page +=4) { // Read returns data for 4 pages at a time. + // Read pages + byteCount = sizeof(buffer); + status = MIFARE_Read(page, buffer, &byteCount); + if (status != STATUS_OK) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + break; + } + // Dump data + for (byte offset = 0; offset < 4; offset++) { + i = page + offset; + if(i < 10) + Serial.print(F(" ")); // Pad with spaces + else + Serial.print(F(" ")); // Pad with spaces + Serial.print(i); + Serial.print(F(" ")); + for (byte index = 0; index < 4; index++) { + i = 4 * offset + index; + if(buffer[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(buffer[i], HEX); + } + Serial.println(); + } + } +} // End PICC_DumpMifareUltralightToSerial() + +/** + * Calculates the bit pattern needed for the specified access bits. In the [C1 C2 C3] tuples 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 = sizeof(response); + MFRC522::StatusCode status = PCD_TransceiveData(&cmd, (byte)1, response, &received, &validBits, (byte)0, false); // 40 + if(status != STATUS_OK) { + if(logErrors) { + Serial.println(F("Card did not respond to 0x40 after HALT command. Are you sure it is a UID changeable one?")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x40 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" 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) { + Serial.println(F("Error in communication at command 0x43, after successfully executing 0x40")); + Serial.print(F("Error name: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + if (received != 1 || response[0] != 0x0A) { + if (logErrors) { + Serial.print(F("Got bad response on backdoor 0x43 command: ")); + Serial.print(response[0], HEX); + Serial.print(F(" (")); + Serial.print(validBits); + Serial.print(F(" 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) { + Serial.println(F("New UID buffer empty, size 0, or size > 15 given")); + } + return false; + } + + // Authenticate for reading + MIFARE_Key key = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + MFRC522::StatusCode 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()) { + Serial.println(F("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) { + Serial.println(F("Failed to authenticate to card for reading, could not set UID: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + } + else { + if (logErrors) { + Serial.print(F("PCD_Authenticate() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + 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) { + Serial.print(F("MIFARE_Read() failed: ")); + Serial.println(GetStatusCodeName(status)); + Serial.println(F("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 (uint8_t 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) { + Serial.println(F("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) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + 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, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + // Write modified block 0 back to card + MFRC522::StatusCode status = MIFARE_Write((byte)0, block0_buffer, (byte)16); + if (status != STATUS_OK) { + if (logErrors) { + Serial.print(F("MIFARE_Write() failed: ")); + Serial.println(GetStatusCodeName(status)); + } + return false; + } + return true; +} + +///////////////////////////////////////////////////////////////////////////////////// +// 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); + + // Reset baud rates + PCD_WriteRegister(TxModeReg, 0x00); + PCD_WriteRegister(RxModeReg, 0x00); + // Reset ModWidthReg + PCD_WriteRegister(ModWidthReg, 0x26); + + MFRC522::StatusCode 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() { + MFRC522::StatusCode result = PICC_Select(&uid); + return (result == STATUS_OK); +} // End diff --git a/libraries/MFRC522/src/MFRC522.h b/libraries/MFRC522/src/MFRC522.h new file mode 100644 index 0000000..83ead46 --- /dev/null +++ b/libraries/MFRC522/src/MFRC522.h @@ -0,0 +1,375 @@ +/** + * Library to use Arduino MFRC522 module. + * + * @authors Dr.Leong, Miguel Balboa, Søren Thing Andersen, Tom Clement, many more! See GitLog. + * + * For more information read the README. + * + * Please read this file for an overview and then MFRC522.cpp for comments on the specific functions. + */ +#ifndef MFRC522_h +#define MFRC522_h + +#include "require_cpp11.h" +#include "deprecated.h" +// Enable integer limits +#define __STDC_LIMIT_MACROS +#include +#include +#include + +#ifndef MFRC522_SPICLOCK +#define MFRC522_SPICLOCK (4000000u) // MFRC522 accept upto 10MHz, set to 4MHz. +#endif + +// Firmware data for self-test +// Reference values based on firmware version +// Hint: if needed, you can remove unused self-test data to save flash memory +// +// Version 0.0 (0x90) +// Philips Semiconductors; Preliminary Specification Revision 2.0 - 01 August 2005; 16.1 self-test +const byte MFRC522_firmware_referenceV0_0[] PROGMEM = { + 0x00, 0x87, 0x98, 0x0f, 0x49, 0xFF, 0x07, 0x19, + 0xBF, 0x22, 0x30, 0x49, 0x59, 0x63, 0xAD, 0xCA, + 0x7F, 0xE3, 0x4E, 0x03, 0x5C, 0x4E, 0x49, 0x50, + 0x47, 0x9A, 0x37, 0x61, 0xE7, 0xE2, 0xC6, 0x2E, + 0x75, 0x5A, 0xED, 0x04, 0x3D, 0x02, 0x4B, 0x78, + 0x32, 0xFF, 0x58, 0x3B, 0x7C, 0xE9, 0x00, 0x94, + 0xB4, 0x4A, 0x59, 0x5B, 0xFD, 0xC9, 0x29, 0xDF, + 0x35, 0x96, 0x98, 0x9E, 0x4F, 0x30, 0x32, 0x8D +}; +// Version 1.0 (0x91) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test +const byte MFRC522_firmware_referenceV1_0[] PROGMEM = { + 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 (0x92) +// NXP Semiconductors; Rev. 3.8 - 17 September 2014; 16.1.1 self-test +const byte MFRC522_firmware_referenceV2_0[] PROGMEM = { + 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 +}; +// Clone +// Fudan Semiconductor FM17522 (0x88) +const byte FM17522_firmware_reference[] PROGMEM = { + 0x00, 0xD6, 0x78, 0x8C, 0xE2, 0xAA, 0x0C, 0x18, + 0x2A, 0xB8, 0x7A, 0x7F, 0xD3, 0x6A, 0xCF, 0x0B, + 0xB1, 0x37, 0x63, 0x4B, 0x69, 0xAE, 0x91, 0xC7, + 0xC3, 0x97, 0xAE, 0x77, 0xF4, 0x37, 0xD7, 0x9B, + 0x7C, 0xF5, 0x3C, 0x11, 0x8F, 0x15, 0xC3, 0xD7, + 0xC1, 0x5B, 0x00, 0x2A, 0xD0, 0x75, 0xDE, 0x9E, + 0x51, 0x64, 0xAB, 0x3E, 0xE9, 0x15, 0xB5, 0xAB, + 0x56, 0x9A, 0x98, 0x82, 0x26, 0xEA, 0x2A, 0x62 +}; + +class MFRC522 { +public: + // Size of the MFRC522 FIFO + static constexpr byte FIFO_SIZE = 64; // The FIFO is 64 bytes. + // Default value for unused pin + static constexpr uint8_t UNUSED_PIN = UINT8_MAX; + + // 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 : byte { + // 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 : byte { + 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 : byte { + 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 : byte { + // 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. + PICC_CMD_RATS = 0xE0, // Request command for Answer To Reset. + // The commands used for MIFARE Classic (from http://www.mouser.com/ds/2/302/MF1S503x-89574.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. + // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered + enum PICC_Type : byte { + PICC_TYPE_UNKNOWN , + PICC_TYPE_ISO_14443_4 , // PICC compliant with ISO/IEC 14443-4 + PICC_TYPE_ISO_18092 , // PICC compliant with ISO/IEC 18092 (NFC) + PICC_TYPE_MIFARE_MINI , // MIFARE Classic protocol, 320 bytes + PICC_TYPE_MIFARE_1K , // MIFARE Classic protocol, 1KB + PICC_TYPE_MIFARE_4K , // MIFARE Classic protocol, 4KB + PICC_TYPE_MIFARE_UL , // MIFARE Ultralight or Ultralight C + PICC_TYPE_MIFARE_PLUS , // MIFARE Plus + PICC_TYPE_MIFARE_DESFIRE, // MIFARE DESFire + PICC_TYPE_TNP3XXX , // Only mentioned in NXP AN 10833 MIFARE Type Identification Procedure + PICC_TYPE_NOT_COMPLETE = 0xff // SAK indicates UID is not complete. + }; + + // Return codes from the functions in this class. Remember to update GetStatusCodeName() if you add more. + // last value set to 0xff, then compiler uses less ram, it seems some optimisations are triggered + enum StatusCode : byte { + STATUS_OK , // Success + STATUS_ERROR , // Error in communication + STATUS_COLLISION , // Collission detected + STATUS_TIMEOUT , // Timeout in communication. + STATUS_NO_ROOM , // A buffer is not big enough. + STATUS_INTERNAL_ERROR , // Internal error in the code. Should not happen ;-) + STATUS_INVALID , // Invalid argument. + STATUS_CRC_WRONG , // The CRC_A does not match + STATUS_MIFARE_NACK = 0xff // 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(). + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for setting up the Arduino + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522(); + MFRC522(byte resetPowerDownPin); + MFRC522(byte chipSelectPin, byte resetPowerDownPin); + + ///////////////////////////////////////////////////////////////////////////////////// + // Basic interface functions for communicating with the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_WriteRegister(PCD_Register reg, byte value); + void PCD_WriteRegister(PCD_Register reg, byte count, byte *values); + byte PCD_ReadRegister(PCD_Register reg); + void PCD_ReadRegister(PCD_Register reg, byte count, byte *values, byte rxAlign = 0); + void PCD_SetRegisterBitMask(PCD_Register reg, byte mask); + void PCD_ClearRegisterBitMask(PCD_Register reg, byte mask); + StatusCode PCD_CalculateCRC(byte *data, byte length, byte *result); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for manipulating the MFRC522 + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_Init(); + void PCD_Init(byte resetPowerDownPin); + void PCD_Init(byte chipSelectPin, byte resetPowerDownPin); + void PCD_Reset(); + void PCD_AntennaOn(); + void PCD_AntennaOff(); + byte PCD_GetAntennaGain(); + void PCD_SetAntennaGain(byte mask); + bool PCD_PerformSelfTest(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Power control functions + ///////////////////////////////////////////////////////////////////////////////////// + void PCD_SoftPowerDown(); + void PCD_SoftPowerUp(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode PCD_TransceiveData(byte *sendData, byte sendLen, byte *backData, byte *backLen, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PCD_CommunicateWithPICC(byte command, byte waitIRq, byte *sendData, byte sendLen, byte *backData = nullptr, byte *backLen = nullptr, byte *validBits = nullptr, byte rxAlign = 0, bool checkCRC = false); + StatusCode PICC_RequestA(byte *bufferATQA, byte *bufferSize); + StatusCode PICC_WakeupA(byte *bufferATQA, byte *bufferSize); + StatusCode PICC_REQA_or_WUPA(byte command, byte *bufferATQA, byte *bufferSize); + virtual StatusCode PICC_Select(Uid *uid, byte validBits = 0); + StatusCode PICC_HaltA(); + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with MIFARE PICCs + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode PCD_Authenticate(byte command, byte blockAddr, MIFARE_Key *key, Uid *uid); + void PCD_StopCrypto1(); + StatusCode MIFARE_Read(byte blockAddr, byte *buffer, byte *bufferSize); + StatusCode MIFARE_Write(byte blockAddr, byte *buffer, byte bufferSize); + StatusCode MIFARE_Ultralight_Write(byte page, byte *buffer, byte bufferSize); + StatusCode MIFARE_Decrement(byte blockAddr, int32_t delta); + StatusCode MIFARE_Increment(byte blockAddr, int32_t delta); + StatusCode MIFARE_Restore(byte blockAddr); + StatusCode MIFARE_Transfer(byte blockAddr); + StatusCode MIFARE_GetValue(byte blockAddr, int32_t *value); + StatusCode MIFARE_SetValue(byte blockAddr, int32_t value); + StatusCode PCD_NTAG216_AUTH(byte *passWord, byte pACK[]); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode 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); + static const __FlashStringHelper *GetStatusCodeName(StatusCode code); + static PICC_Type 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); + static const __FlashStringHelper *PICC_GetTypeName(PICC_Type type); + static const __FlashStringHelper *PICC_GetTypeName(byte type) + { + return PICC_GetTypeName(static_cast(type)); + } + + // Support functions for debuging + void PCD_DumpVersionToSerial(); + void PICC_DumpToSerial(Uid *uid); + void PICC_DumpDetailsToSerial(Uid *uid); + void PICC_DumpMifareClassicToSerial(Uid *uid, PICC_Type piccType, MIFARE_Key *key); + void PICC_DumpMifareClassicSectorToSerial(Uid *uid, MIFARE_Key *key, byte sector); + void PICC_DumpMifareUltralightToSerial(); + + // Advanced functions for MIFARE + 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 + ///////////////////////////////////////////////////////////////////////////////////// + virtual bool PICC_IsNewCardPresent(); + virtual bool PICC_ReadCardSerial(); + +protected: + byte _chipSelectPin; // Arduino pin connected to MFRC522's SPI slave select input (Pin 24, NSS, active low) + byte _resetPowerDownPin; // Arduino pin connected to MFRC522's reset and power down input (Pin 6, NRSTPD, active low) + StatusCode MIFARE_TwoStepHelper(byte command, byte blockAddr, int32_t data); +}; + +#endif diff --git a/libraries/MFRC522/src/MFRC522Extended.cpp b/libraries/MFRC522/src/MFRC522Extended.cpp new file mode 100644 index 0000000..02ef3db --- /dev/null +++ b/libraries/MFRC522/src/MFRC522Extended.cpp @@ -0,0 +1,1160 @@ +/* + * Library extends MFRC522.h to support RATS for ISO-14443-4 PICC. + * RATS - Request for Answer To Select. + * NOTE: Please also check the comments in MFRC522Extended.h + * @author JPG-Consulting +*/ + +#include "MFRC522Extended.h" + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with PICCs +///////////////////////////////////////////////////////////////////////////////////// + +/** + * 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. + */ +MFRC522::StatusCode MFRC522Extended::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; + MFRC522::StatusCode result; + byte count; + byte index; + byte uidIndex; // The first index in uid->uidByte[] that is used in the current Cascade Level. + int8_t currentLevelKnownBits; // The number of known UID bits in the current Cascade Level. + 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 are 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 separate 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. + byte valueOfCollReg = PCD_ReadRegister(CollReg); // CollReg[7..0] bits are: ValuesAfterColl reserved CollPosNotValid CollPos[4:0] + if (valueOfCollReg & 0x20) { // CollPosNotValid + return STATUS_COLLISION; // Without a valid collision position we cannot continue + } + byte collisionPos = valueOfCollReg & 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; + + // IF SAK bit 6 = 1 then it is ISO/IEC 14443-4 (T=CL) + // A Request ATS command should be sent + // We also check SAK bit 3 is cero, as it stands for UID complete (1 would tell us it is incomplete) + if ((uid->sak & 0x24) == 0x20) { + Ats ats; + result = PICC_RequestATS(&ats); + if (result == STATUS_OK) { + // Check the ATS + if (ats.size > 0) + { + // TA1 has been transmitted? + // PPS must be supported... + if (ats.ta1.transmitted) + { + // TA1 + // 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | Description + // ---+---+---+---+---+---+---+---+------------------------------------------ + // 0 | - | - | - | 0 | - | - | - | Different D for each direction supported + // 1 | - | - | - | 0 | - | - | - | Only same D for both direction supported + // - | x | x | x | 0 | - | - | - | DS (Send D) + // - | - | - | - | 0 | x | x | x | DR (Receive D) + // + // D to bitrate table + // 3 | 2 | 1 | Value + // ---+---+---+----------------------------- + // 1 | - | - | 848 kBaud is supported + // - | 1 | - | 424 kBaud is supported + // - | - | 1 | 212 kBaud is supported + // 0 | 0 | 0 | Only 106 kBaud is supported + // + // Note: 106 kBaud is always supported + // + // I have almost constant timeouts when changing speeds :( + // default never used, so only delarate + //TagBitRates ds = BITRATE_106KBITS; + //TagBitRates dr = BITRATE_106KBITS; + TagBitRates ds; + TagBitRates dr; + + //// TODO Not working at 848 or 424 + //if (ats.ta1.ds & 0x04) + //{ + // ds = BITRATE_848KBITS; + //} + //else if (ats.ta1.ds & 0x02) + //{ + // ds = BITRATE_424KBITS; + //} + //else if (ats.ta1.ds & 0x01) + //{ + // ds = BITRATE_212KBITS; + //} + //else + //{ + // ds = BITRATE_106KBITS; + //} + + if (ats.ta1.ds & 0x01) + { + ds = BITRATE_212KBITS; + } + else + { + ds = BITRATE_106KBITS; + } + + //// Not working at 848 or 424 + //if (ats.ta1.dr & 0x04) + //{ + // dr = BITRATE_848KBITS; + //} + //else if (ats.ta1.dr & 0x02) + //{ + // dr = BITRATE_424KBITS; + //} + //else if (ats.ta1.dr & 0x01) + //{ + // dr = BITRATE_212KBITS; + //} + //else + //{ + // dr = BITRATE_106KBITS; + //} + + if (ats.ta1.dr & 0x01) + { + dr = BITRATE_212KBITS; + } + else + { + dr = BITRATE_106KBITS; + } + + PICC_PPS(ds, dr); + } + } + } + } + + return STATUS_OK; +} // End PICC_Select() + +/** + * Transmits a Request command for Answer To Select (ATS). + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522Extended::PICC_RequestATS(Ats *ats) +{ + // TODO unused variable + //byte count; + MFRC522::StatusCode result; + + byte bufferATS[FIFO_SIZE]; + byte bufferSize = FIFO_SIZE; + + memset(bufferATS, 0, FIFO_SIZE); + + // Build command buffer + bufferATS[0] = PICC_CMD_RATS; + + // The CID defines the logical number of the addressed card and has a range of 0 + // through 14; 15 is reserved for future use (RFU). + // + // FSDI codes the maximum frame size (FSD) that the terminal can receive. + // + // FSDI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F + // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- + // FSD (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 + // + bufferATS[1] = 0x50; // FSD=64, CID=0 + + // Calculate CRC_A + result = PCD_CalculateCRC(bufferATS, 2, &bufferATS[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(bufferATS, 4, bufferATS, &bufferSize, NULL, 0, true); + if (result != STATUS_OK) { + PICC_HaltA(); + } + + // Set the ats structure data + ats->size = bufferATS[0]; + + // T0 byte: + // + // b8 | b7 | b6 | b5 | b4 | b3 | b2 | b1 | Meaning + //----+----+----+----+----+----+----+----+--------------------------- + // 0 | ...| ...| ...| ...|... | ...| ...| Set to 0 (RFU) + // 0 | 1 | x | x | ...|... | ...| ...| TC1 transmitted + // 0 | x | 1 | x | ...|... | ...| ...| TB1 transmitted + // 0 | x | x | 1 | ...|... | ...| ...| TA1 transmitted + // 0 | ...| ...| ...| x | x | x | x | Maximum frame size (FSCI) + // + // FSCI | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9-F + // ------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+----------- + // FSC (bytes) | 16 | 24 | 32 | 40 | 48 | 64 | 96 | 128 | 256 | RFU > 256 + // + // Default FSCI is 2 (32 bytes) + if (ats->size > 0x01) + { + // TC1, TB1 and TA1 where NOT transmitted + ats->ta1.transmitted = (bool)(bufferATS[1] & 0x40); + ats->tb1.transmitted = (bool)(bufferATS[1] & 0x20); + ats->tc1.transmitted = (bool)(bufferATS[1] & 0x10); + + // Decode FSCI + switch (bufferATS[1] & 0x0F) + { + case 0x00: + ats->fsc = 16; + break; + case 0x01: + ats->fsc = 24; + break; + case 0x02: + ats->fsc = 32; + break; + case 0x03: + ats->fsc = 40; + break; + case 0x04: + ats->fsc = 48; + break; + case 0x05: + ats->fsc = 64; + break; + case 0x06: + ats->fsc = 96; + break; + case 0x07: + ats->fsc = 128; + break; + case 0x08: + // This value cannot be hold by a byte + // The reason I ignore it is that MFRC255 FIFO is 64 bytes so this is not a possible value (or atleast it shouldn't) + //ats->fsc = 256; + break; + // TODO: What to do with RFU (Reserved for future use)? + default: + break; + } + + // TA1 + if (ats->ta1.transmitted) + { + ats->ta1.sameD = (bool)(bufferATS[2] & 0x80); + ats->ta1.ds = (TagBitRates)((bufferATS[2] & 0x70) >> 4); + ats->ta1.dr = (TagBitRates)(bufferATS[2] & 0x07); + } + else + { + // Default TA1 + ats->ta1.ds = BITRATE_106KBITS; + ats->ta1.dr = BITRATE_106KBITS; + } + + // TB1 + if (ats->tb1.transmitted) + { + uint8_t tb1Index = 2; + + if (ats->ta1.transmitted) + tb1Index++; + + ats->tb1.fwi = (bufferATS[tb1Index] & 0xF0) >> 4; + ats->tb1.sfgi = bufferATS[tb1Index] & 0x0F; + } + else + { + // Defaults for TB1 + ats->tb1.fwi = 0; // TODO: Don't know the default for this! + ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + } + + // TC1 + if (ats->tc1.transmitted) + { + uint8_t tc1Index = 2; + + if (ats->ta1.transmitted) + tc1Index++; + if (ats->tb1.transmitted) + tc1Index++; + + ats->tc1.supportsCID = (bool)(bufferATS[tc1Index] & 0x02); + ats->tc1.supportsNAD = (bool)(bufferATS[tc1Index] & 0x01); + } + else + { + // Defaults for TC1 + ats->tc1.supportsCID = true; + ats->tc1.supportsNAD = false; + } + } + else + { + // TC1, TB1 and TA1 where NOT transmitted + ats->ta1.transmitted = false; + ats->tb1.transmitted = false; + ats->tc1.transmitted = false; + + // Default FSCI + ats->fsc = 32; // Defaults to FSCI 2 (32 bytes) + + // Default TA1 + ats->ta1.sameD = false; + ats->ta1.ds = BITRATE_106KBITS; + ats->ta1.dr = BITRATE_106KBITS; + + // Defaults for TB1 + ats->tb1.transmitted = false; + ats->tb1.fwi = 0; // TODO: Don't know the default for this! + ats->tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + + // Defaults for TC1 + ats->tc1.transmitted = false; + ats->tc1.supportsCID = true; + ats->tc1.supportsNAD = false; + } + + memcpy(ats->data, bufferATS, bufferSize - 2); + + return result; +} // End PICC_RequestATS() + +/** + * Transmits Protocol and Parameter Selection Request (PPS) without parameter 1 + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522Extended::PICC_PPS() +{ + StatusCode result; + + byte ppsBuffer[4]; + byte ppsBufferSize = 4; + // Start byte: The start byte (PPS) consists of two parts: + // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. + // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. + ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS + ppsBuffer[1] = 0x00; // PPS0 indicates whether PPS1 is present + + // Calculate CRC_A + result = PCD_CalculateCRC(ppsBuffer, 2, &ppsBuffer[2]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(ppsBuffer, 4, ppsBuffer, &ppsBufferSize, NULL, 0, true); + if (result == STATUS_OK) + { + // Enable CRC for T=CL + byte txReg = PCD_ReadRegister(TxModeReg) | 0x80; + byte rxReg = PCD_ReadRegister(RxModeReg) | 0x80; + + PCD_WriteRegister(TxModeReg, txReg); + PCD_WriteRegister(RxModeReg, rxReg); + } + + return result; +} // End PICC_PPS() + +/** + * Transmits Protocol and Parameter Selection Request (PPS) + * + * @return STATUS_OK on success, STATUS_??? otherwise. + */ +MFRC522::StatusCode MFRC522Extended::PICC_PPS(TagBitRates sendBitRate, ///< DS + TagBitRates receiveBitRate ///< DR +) { + StatusCode result; + + // TODO not used + //byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; + //byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; + + byte ppsBuffer[5]; + byte ppsBufferSize = 5; + // Start byte: The start byte (PPS) consists of two parts: + // –The upper nibble(b8–b5) is set to’D'to identify the PPS. All other values are RFU. + // -The lower nibble(b4–b1), which is called the ‘card identifier’ (CID), defines the logical number of the addressed card. + ppsBuffer[0] = 0xD0; // CID is hardcoded as 0 in RATS + ppsBuffer[1] = 0x11; // PPS0 indicates whether PPS1 is present + + // Bit 8 - Set to '0' as MFRC522 allows different bit rates for send and receive + // Bit 4 - Set to '0' as it is Reserved for future use. + //ppsBuffer[2] = (((sendBitRate & 0x03) << 4) | (receiveBitRate & 0x03)) & 0xE7; + ppsBuffer[2] = (((sendBitRate & 0x03) << 2) | (receiveBitRate & 0x03)) & 0xE7; + + // Calculate CRC_A + result = PCD_CalculateCRC(ppsBuffer, 3, &ppsBuffer[3]); + if (result != STATUS_OK) { + return result; + } + + // Transmit the buffer and receive the response, validate CRC_A. + result = PCD_TransceiveData(ppsBuffer, 5, ppsBuffer, &ppsBufferSize, NULL, 0, true); + if (result == STATUS_OK) + { + // Make sure it is an answer to our PPS + // We should receive our PPS byte and 2 CRC bytes + if ((ppsBufferSize == 3) && (ppsBuffer[0] == 0xD0)) { + byte txReg = PCD_ReadRegister(TxModeReg) & 0x8F; + byte rxReg = PCD_ReadRegister(RxModeReg) & 0x8F; + + // Set bit rate and enable CRC for T=CL + txReg = (txReg & 0x8F) | ((receiveBitRate & 0x03) << 4) | 0x80; + rxReg = (rxReg & 0x8F) | ((sendBitRate & 0x03) << 4) | 0x80; + rxReg &= 0xF0; //Enforce although this should be set already + + // From ConfigIsoType + //rxReg |= 0x06; + + PCD_WriteRegister(TxModeReg, txReg); + PCD_WriteRegister(RxModeReg, rxReg); + + // At 212kBps + switch (sendBitRate) { + case BITRATE_212KBITS: + { + //PCD_WriteRegister(ModWidthReg, 0x13); + PCD_WriteRegister(ModWidthReg, 0x15); + } + break; + case BITRATE_424KBITS: + { + PCD_WriteRegister(ModWidthReg, 0x0A); + } + break; + case BITRATE_848KBITS: + { + PCD_WriteRegister(ModWidthReg, 0x05); + } + break; + default: + { + PCD_WriteRegister(ModWidthReg, 0x26); // Default value + } + break; + } + + //PCD_WriteRegister(RxThresholdReg, 0x84); // ISO-14443.4 Type A (default) + //PCD_WriteRegister(ControlReg, 0x10); + + delayMicroseconds(10); + } + else + { + return STATUS_ERROR; + } + } + + return result; +} // End PICC_PPS() + + +///////////////////////////////////////////////////////////////////////////////////// +// Functions for communicating with ISO/IEC 14433-4 cards +///////////////////////////////////////////////////////////////////////////////////// + +MFRC522::StatusCode MFRC522Extended::TCL_Transceive(PcbBlock *send, PcbBlock *back) +{ + MFRC522::StatusCode result; + byte inBuffer[FIFO_SIZE]; + byte inBufferSize = FIFO_SIZE; + byte outBuffer[send->inf.size + 5]; // PCB + CID + NAD + INF + EPILOGUE (CRC) + byte outBufferOffset = 1; + byte inBufferOffset = 1; + + // Set the PCB byte + outBuffer[0] = send->prologue.pcb; + + // Set the CID byte if available + if (send->prologue.pcb & 0x08) { + outBuffer[outBufferOffset] = send->prologue.cid; + outBufferOffset++; + } + + // Set the NAD byte if available + if (send->prologue.pcb & 0x04) { + outBuffer[outBufferOffset] = send->prologue.nad; + outBufferOffset++; + } + + // Copy the INF field if available + if (send->inf.size > 0) { + memcpy(&outBuffer[outBufferOffset], send->inf.data, send->inf.size); + outBufferOffset += send->inf.size; + } + + // Is the CRC enabled for transmission? + byte txModeReg = PCD_ReadRegister(TxModeReg); + if ((txModeReg & 0x80) != 0x80) { + // Calculate CRC_A + result = PCD_CalculateCRC(outBuffer, outBufferOffset, &outBuffer[outBufferOffset]); + if (result != STATUS_OK) { + return result; + } + + outBufferOffset += 2; + } + + // Transceive the block + result = PCD_TransceiveData(outBuffer, outBufferOffset, inBuffer, &inBufferSize); + if (result != STATUS_OK) { + return result; + } + + // We want to turn the received array back to a PcbBlock + back->prologue.pcb = inBuffer[0]; + + // CID byte is present? + if (send->prologue.pcb & 0x08) { + back->prologue.cid = inBuffer[inBufferOffset]; + inBufferOffset++; + } + + // NAD byte is present? + if (send->prologue.pcb & 0x04) { + back->prologue.nad = inBuffer[inBufferOffset]; + inBufferOffset++; + } + + // Check if CRC is taken care of by MFRC522 + byte rxModeReg = PCD_ReadRegister(TxModeReg); + if ((rxModeReg & 0x80) != 0x80) { + Serial.print("CRC is not taken care of by MFRC522: "); + Serial.println(rxModeReg, HEX); + + // Check the CRC + // We need at least the CRC_A value. + if ((int)(inBufferSize - inBufferOffset) < 2) { + return STATUS_CRC_WRONG; + } + + // Verify CRC_A - do our own calculation and store the control in controlBuffer. + byte controlBuffer[2]; + MFRC522::StatusCode status = PCD_CalculateCRC(inBuffer, inBufferSize - 2, controlBuffer); + if (status != STATUS_OK) { + return status; + } + + if ((inBuffer[inBufferSize - 2] != controlBuffer[0]) || (inBuffer[inBufferSize - 1] != controlBuffer[1])) { + return STATUS_CRC_WRONG; + } + + // Take away the CRC bytes + inBufferSize -= 2; + } + + // Got more data? + if (inBufferSize > inBufferOffset) { + if ((inBufferSize - inBufferOffset) > back->inf.size) { + return STATUS_NO_ROOM; + } + + memcpy(back->inf.data, &inBuffer[inBufferOffset], inBufferSize - inBufferOffset); + back->inf.size = inBufferSize - inBufferOffset; + } else { + back->inf.size = 0; + } + + // If the response is a R-Block check NACK + if (((inBuffer[0] & 0xC0) == 0x80) && (inBuffer[0] & 0x20)) { + return STATUS_MIFARE_NACK; + } + + return result; +} +/** + * Send an I-Block (Application) + */ +MFRC522::StatusCode MFRC522Extended::TCL_Transceive(TagInfo *tag, byte *sendData, byte sendLen, byte *backData, byte *backLen) +{ + MFRC522::StatusCode result; + + PcbBlock out; + PcbBlock in; + byte outBuffer[FIFO_SIZE]; + byte outBufferSize = FIFO_SIZE; + byte totalBackLen = *backLen; + + // This command sends an I-Block + out.prologue.pcb = 0x02; + + if (tag->ats.tc1.supportsCID) { + out.prologue.pcb |= 0x08; + out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 + } + + // This command doe not support NAD + out.prologue.pcb &= 0xFB; + out.prologue.nad = 0x00; + + // Set the block number + if (tag->blockNumber) { + out.prologue.pcb |= 0x01; + } + + // Do we have data to send? + if (sendData && (sendLen > 0)) { + out.inf.size = sendLen; + out.inf.data = sendData; + } else { + out.inf.size = 0; + out.inf.data = NULL; + } + + // Initialize the receiving data + // TODO Warning: Value escapes the local scope + in.inf.data = outBuffer; + in.inf.size = outBufferSize; + + result = TCL_Transceive(&out, &in); + if (result != STATUS_OK) { + return result; + } + + // Swap block number on success + tag->blockNumber = !tag->blockNumber; + + if (backData && backLen) { + if (*backLen < in.inf.size) + return STATUS_NO_ROOM; + + *backLen = in.inf.size; + memcpy(backData, in.inf.data, in.inf.size); + } + + // Check chaining + if ((in.prologue.pcb & 0x10) == 0x00) + return result; + + // Result is chained + // Send an ACK to receive more data + // TODO: Should be checked I've never needed to send an ACK + while (in.prologue.pcb & 0x10) { + byte ackData[FIFO_SIZE]; + byte ackDataSize = FIFO_SIZE; + + result = TCL_TransceiveRBlock(tag, true, ackData, &ackDataSize); + if (result != STATUS_OK) + return result; + + if (backData && backLen) { + if ((*backLen + ackDataSize) > totalBackLen) + return STATUS_NO_ROOM; + + memcpy(&(backData[*backLen]), ackData, ackDataSize); + *backLen += ackDataSize; + } + } + + return result; +} // End TCL_Transceive() + +/** + * Send R-Block to the PICC. + */ +MFRC522::StatusCode MFRC522Extended::TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData, byte *backLen) +{ + MFRC522::StatusCode result; + + PcbBlock out; + PcbBlock in; + byte outBuffer[FIFO_SIZE]; + byte outBufferSize = FIFO_SIZE; + + // This command sends an R-Block + if (ack) + out.prologue.pcb = 0xA2; // ACK + else + out.prologue.pcb = 0xB2; // NAK + + + if (tag->ats.tc1.supportsCID) { + out.prologue.pcb |= 0x08; + out.prologue.cid = 0x00; // CID is curentlly hardcoded as 0x00 + } + + // This command doe not support NAD + out.prologue.pcb &= 0xFB; + out.prologue.nad = 0x00; + + // Set the block number + if (tag->blockNumber) { + out.prologue.pcb |= 0x01; + } + + // No INF data for R-Block + out.inf.size = 0; + out.inf.data = NULL; + + // Initialize the receiving data + // TODO Warning: Value escapes the local scope + in.inf.data = outBuffer; + in.inf.size = outBufferSize; + + result = TCL_Transceive(&out, &in); + if (result != STATUS_OK) { + return result; + } + + // Swap block number on success + tag->blockNumber = !tag->blockNumber; + + if (backData && backLen) { + if (*backLen < in.inf.size) + return STATUS_NO_ROOM; + + *backLen = in.inf.size; + memcpy(backData, in.inf.data, in.inf.size); + } + + return result; +} // End TCL_TransceiveRBlock() + +/** + * Send an S-Block to deselect the card. + */ +MFRC522::StatusCode MFRC522Extended::TCL_Deselect(TagInfo *tag) +{ + MFRC522::StatusCode result; + byte outBuffer[4]; + byte outBufferSize = 1; + byte inBuffer[FIFO_SIZE]; + byte inBufferSize = FIFO_SIZE; + + outBuffer[0] = 0xC2; + if (tag->ats.tc1.supportsCID) + { + outBuffer[0] |= 0x08; + outBuffer[1] = 0x00; // CID is hardcoded + outBufferSize = 2; + } + + result = PCD_TransceiveData(outBuffer, outBufferSize, inBuffer, &inBufferSize); + if (result != STATUS_OK) { + return result; + } + + // TODO:Maybe do some checks? In my test it returns: CA 00 (Same data as I sent to my card) + + return result; +} // End TCL_Deselect() + +///////////////////////////////////////////////////////////////////////////////////// +// Support functions +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Get the PICC type. + * + * @return PICC_Type + */ +MFRC522::PICC_Type MFRC522Extended::PICC_GetType(TagInfo *tag ///< The TagInfo returned from PICC_Select(). +) { + // http://www.nxp.com/documents/application_note/AN10833.pdf + // 3.2 Coding of Select Acknowledge (SAK) + // ignore 8-bit (iso14443 starts with LSBit = bit 1) + // fixes wrong type for manufacturer Infineon (http://nfc-tools.org/index.php?title=ISO14443A) + byte sak = tag->uid.sak & 0x7F; + switch (sak) { + case 0x04: return PICC_TYPE_NOT_COMPLETE; // UID not complete + case 0x09: return PICC_TYPE_MIFARE_MINI; + case 0x08: return PICC_TYPE_MIFARE_1K; + case 0x18: return PICC_TYPE_MIFARE_4K; + case 0x00: return PICC_TYPE_MIFARE_UL; + case 0x10: + case 0x11: return PICC_TYPE_MIFARE_PLUS; + case 0x01: return PICC_TYPE_TNP3XXX; + case 0x20: + if (tag->atqa == 0x0344) + return PICC_TYPE_MIFARE_DESFIRE; + return PICC_TYPE_ISO_14443_4; + case 0x40: return PICC_TYPE_ISO_18092; + default: return PICC_TYPE_UNKNOWN; + } +} // End PICC_GetType() + +/** + * 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 MFRC522Extended::PICC_DumpToSerial(TagInfo *tag) +{ + MIFARE_Key key; + + // Dump UID, SAK and Type + PICC_DumpDetailsToSerial(tag); + + // Dump contents + PICC_Type piccType = MFRC522::PICC_GetType(tag->uid.sak); + 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(&tag->uid, piccType, &key); + break; + + case PICC_TYPE_MIFARE_UL: + PICC_DumpMifareUltralightToSerial(); + break; + + case PICC_TYPE_ISO_14443_4: + case PICC_TYPE_MIFARE_DESFIRE: + PICC_DumpISO14443_4(tag); + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + case PICC_TYPE_ISO_18092: + case PICC_TYPE_MIFARE_PLUS: + case PICC_TYPE_TNP3XXX: + Serial.println(F("Dumping memory contents not implemented for that PICC type.")); + break; + + case PICC_TYPE_UNKNOWN: + case PICC_TYPE_NOT_COMPLETE: + default: + break; // No memory dump here + } + + Serial.println(); + PICC_HaltA(); // Already done if it was a MIFARE Classic PICC. +} + +/** + * Dumps card info (UID,SAK,Type) about the selected PICC to Serial. + */ +void MFRC522Extended::PICC_DumpDetailsToSerial(TagInfo *tag ///< Pointer to TagInfo struct returned from a successful PICC_Select(). +) { + // ATQA + Serial.print(F("Card ATQA:")); + if (((tag->atqa & 0xFF00u) >> 8) < 0x10) + Serial.print(F(" 0")); + Serial.print((tag->atqa & 0xFF00u) >> 8, HEX); + if ((tag->atqa & 0x00FFu) < 0x10) + Serial.print(F("0")); + else + Serial.print(F(" ")); + Serial.println(tag->atqa & 0x00FFu, HEX); + + // UID + Serial.print(F("Card UID:")); + for (byte i = 0; i < tag->uid.size; i++) { + if (tag->uid.uidByte[i] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(tag->uid.uidByte[i], HEX); + } + Serial.println(); + + // SAK + Serial.print(F("Card SAK: ")); + if (tag->uid.sak < 0x10) + Serial.print(F("0")); + Serial.println(tag->uid.sak, HEX); + + // (suggested) PICC type + PICC_Type piccType = PICC_GetType(tag); + Serial.print(F("PICC type: ")); + Serial.println(PICC_GetTypeName(piccType)); +} // End PICC_DumpDetailsToSerial() + +/** + * Dumps memory contents of a ISO-14443-4 PICC. + */ +void MFRC522Extended::PICC_DumpISO14443_4(TagInfo *tag) +{ + // ATS + if (tag->ats.size > 0x00) { // The first byte is the ATS length including the length byte + Serial.print(F("Card ATS:")); + for (byte offset = 0; offset < tag->ats.size; offset++) { + if (tag->ats.data[offset] < 0x10) + Serial.print(F(" 0")); + else + Serial.print(F(" ")); + Serial.print(tag->ats.data[offset], HEX); + } + Serial.println(); + } + +} // End PICC_DumpISO14443_4 + +///////////////////////////////////////////////////////////////////////////////////// +// 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 MFRC522Extended::PICC_IsNewCardPresent() { + byte bufferATQA[2]; + byte bufferSize = sizeof(bufferATQA); + + // Reset baud rates + PCD_WriteRegister(TxModeReg, 0x00); + PCD_WriteRegister(RxModeReg, 0x00); + // Reset ModWidthReg + PCD_WriteRegister(ModWidthReg, 0x26); + + MFRC522::StatusCode result = PICC_RequestA(bufferATQA, &bufferSize); + + if (result == STATUS_OK || result == STATUS_COLLISION) { + tag.atqa = ((uint16_t)bufferATQA[1] << 8) | bufferATQA[0]; + tag.ats.size = 0; + tag.ats.fsc = 32; // default FSC value + + // Defaults for TA1 + tag.ats.ta1.transmitted = false; + tag.ats.ta1.sameD = false; + tag.ats.ta1.ds = MFRC522Extended::BITRATE_106KBITS; + tag.ats.ta1.dr = MFRC522Extended::BITRATE_106KBITS; + + // Defaults for TB1 + tag.ats.tb1.transmitted = false; + tag.ats.tb1.fwi = 0; // TODO: Don't know the default for this! + tag.ats.tb1.sfgi = 0; // The default value of SFGI is 0 (meaning that the card does not need any particular SFGT) + + // Defaults for TC1 + tag.ats.tc1.transmitted = false; + tag.ats.tc1.supportsCID = true; + tag.ats.tc1.supportsNAD = false; + + memset(tag.ats.data, 0, FIFO_SIZE - 2); + + tag.blockNumber = false; + return true; + } + return false; +} // 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 MFRC522Extended::PICC_ReadCardSerial() { + MFRC522::StatusCode result = PICC_Select(&tag.uid); + + // Backward compatibility + uid.size = tag.uid.size; + uid.sak = tag.uid.sak; + memcpy(uid.uidByte, tag.uid.uidByte, sizeof(tag.uid.uidByte)); + + return (result == STATUS_OK); +} // End diff --git a/libraries/MFRC522/src/MFRC522Extended.h b/libraries/MFRC522/src/MFRC522Extended.h new file mode 100644 index 0000000..9fd8ff8 --- /dev/null +++ b/libraries/MFRC522/src/MFRC522Extended.h @@ -0,0 +1,120 @@ +/** + * Library extends MFRC522.h to support RATS for ISO-14443-4 PICC. + * RATS - Request for Answer To Select. + * @author JPG-Consulting + */ +#ifndef MFRC522Extended_h +#define MFRC522Extended_h + +#include +#include "MFRC522.h" + +class MFRC522Extended : public MFRC522 { + +public: + // ISO/IEC 14443-4 bit rates + enum TagBitRates : byte { + BITRATE_106KBITS = 0x00, + BITRATE_212KBITS = 0x01, + BITRATE_424KBITS = 0x02, + BITRATE_848KBITS = 0x03 + }; + + // Structure to store ISO/IEC 14443-4 ATS + typedef struct { + byte size; + byte fsc; // Frame size for proximity card + + struct { + bool transmitted; + bool sameD; // Only the same D for both directions supported + TagBitRates ds; // Send D + TagBitRates dr; // Receive D + } ta1; + + struct { + bool transmitted; + byte fwi; // Frame waiting time integer + byte sfgi; // Start-up frame guard time integer + } tb1; + + struct { + bool transmitted; + bool supportsCID; + bool supportsNAD; + } tc1; + + // Raw data from ATS + byte data[FIFO_SIZE - 2]; // ATS cannot be bigger than FSD - 2 bytes (CRC), according to ISO 14443-4 5.2.2 + } Ats; + + // A struct used for passing the PICC information + typedef struct { + uint16_t atqa; + Uid uid; + Ats ats; + + // For Block PCB + bool blockNumber; + } TagInfo; + + // A struct used for passing PCB Block + typedef struct { + struct { + byte pcb; + byte cid; + byte nad; + } prologue; + struct { + byte size; + byte *data; + } inf; + } PcbBlock; + + // Member variables + TagInfo tag; + + ///////////////////////////////////////////////////////////////////////////////////// + // Contructors + ///////////////////////////////////////////////////////////////////////////////////// + MFRC522Extended() : MFRC522() {}; + MFRC522Extended(uint8_t rst) : MFRC522(rst) {}; + MFRC522Extended(uint8_t ss, uint8_t rst) : MFRC522(ss, rst) {}; + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with PICCs + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode PICC_Select(Uid *uid, byte validBits = 0) override; // overrride + StatusCode PICC_RequestATS(Ats *ats); + StatusCode PICC_PPS(); // PPS command without bitrate parameter + StatusCode PICC_PPS(TagBitRates sendBitRate, TagBitRates receiveBitRate); // Different D values + + ///////////////////////////////////////////////////////////////////////////////////// + // Functions for communicating with ISO/IEC 14433-4 cards + ///////////////////////////////////////////////////////////////////////////////////// + StatusCode TCL_Transceive(PcbBlock *send, PcbBlock *back); + StatusCode TCL_Transceive(TagInfo * tag, byte *sendData, byte sendLen, byte *backData = NULL, byte *backLen = NULL); + StatusCode TCL_TransceiveRBlock(TagInfo *tag, bool ack, byte *backData = NULL, byte *backLen = NULL); + StatusCode TCL_Deselect(TagInfo *tag); + + ///////////////////////////////////////////////////////////////////////////////////// + // Support functions + ///////////////////////////////////////////////////////////////////////////////////// + static PICC_Type PICC_GetType(TagInfo *tag); + using MFRC522::PICC_GetType;// // make old PICC_GetType(byte sak) available, otherwise would be hidden by PICC_GetType(TagInfo *tag) + + // Support functions for debuging + void PICC_DumpToSerial(TagInfo *tag); + using MFRC522::PICC_DumpToSerial; // make old PICC_DumpToSerial(Uid *uid) available, otherwise would be hidden by PICC_DumpToSerial(TagInfo *tag) + void PICC_DumpDetailsToSerial(TagInfo *tag); + using MFRC522::PICC_DumpDetailsToSerial; // make old PICC_DumpDetailsToSerial(Uid *uid) available, otherwise would be hidden by PICC_DumpDetailsToSerial(TagInfo *tag) + void PICC_DumpISO14443_4(TagInfo *tag); + + ///////////////////////////////////////////////////////////////////////////////////// + // Convenience functions - does not add extra functionality + ///////////////////////////////////////////////////////////////////////////////////// + bool PICC_IsNewCardPresent() override; // overrride + bool PICC_ReadCardSerial() override; // overrride +}; + +#endif diff --git a/libraries/MFRC522/src/deprecated.h b/libraries/MFRC522/src/deprecated.h new file mode 100644 index 0000000..cba2cf2 --- /dev/null +++ b/libraries/MFRC522/src/deprecated.h @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2016 by Ludwig Grill (www.rotzbua.de) + * Simple deprecated workaround for Arduino IDE + * IDE 1.6.8 use gcc 4.8 which do not support c++14 [[deprecated]] + * Later versions should support c++14, then use c++14 syntax + */ +#ifndef DEPRECATED_H +#define DEPRECATED_H + +#ifdef __has_cpp_attribute +#if __has_cpp_attribute(deprecated) +#define DEPRECATED [[deprecated]] +#define DEPRECATED_MSG(msg) [[deprecated(msg)]] +#endif // __has_cpp_attribute(deprecated) +#else +#define DEPRECATED __attribute__((deprecated)) +#define DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) +#endif // __has_cpp_attribute + +#endif // DEPRECATED_H diff --git a/libraries/MFRC522/src/require_cpp11.h b/libraries/MFRC522/src/require_cpp11.h new file mode 100644 index 0000000..6561cd8 --- /dev/null +++ b/libraries/MFRC522/src/require_cpp11.h @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2016 by Ludwig Grill (www.rotzbua.de) + * Throws error if c++11 is not supported + */ +#ifndef REQUIRE_CPP11_H +#define REQUIRE_CPP11_H + +#if __cplusplus < 201103L +#error "This library needs at least a C++11 compliant compiler, maybe compiler argument for C++11 support is missing or if you use Arduino IDE upgrade to version >=1.6.6" +#endif + +#endif // REQUIRE_CPP11_H diff --git a/libraries/NeoPixel/LICENSE.txt b/libraries/NeoPixel/LICENSE.txt new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/libraries/NeoPixel/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/libraries/NeoPixel/README.md b/libraries/NeoPixel/README.md new file mode 100644 index 0000000..4991d44 --- /dev/null +++ b/libraries/NeoPixel/README.md @@ -0,0 +1,2 @@ + +## NeoPixel - это библиотека адресного светодиода WS2812 для Амура (МК от Микрон) в ArduinoIDE diff --git a/libraries/NeoPixel/examples/simple/simple.ino b/libraries/NeoPixel/examples/simple/simple.ino new file mode 100644 index 0000000..eb42d35 --- /dev/null +++ b/libraries/NeoPixel/examples/simple/simple.ino @@ -0,0 +1,42 @@ +#include + +#define PIN 2 +#define NUMPIXELS 8 + +NeoPixel pixels(NUMPIXELS, PIN); + +uint8_t red = 0xff; +uint8_t green = 0x00; +uint8_t blue = 0x00; +uint8_t white = 0x00; + +void setup() { + // init pin for led control + pixels.begin(); +} + +void loop() { + // clear pixels colors + pixels.clear(); + + // set new color and show it + for(int i=0; i + +#define PIN 2 +#define NUMPIXELS 8 + +NeoPixel pixels(NUMPIXELS, PIN); + +void setup() { + // init Serial and pin for led control + Serial.begin(9600); + pixels.begin(); + Serial.println("pixels.begin"); + delay(1000); + // clear pixels and show + pixels.clear(); + pixels.show(); + Serial.println("pixels.clear"); + delay(1000); +} + +void loop() { + // sequentially set a new pixel color and show it + for(int i=0; i +sentence=Arduino library for controlling single-wire-based LED pixels and strip. +paragraph=This library can manage tapes with WS2812 address LEDs - up to 60 pieces. +category=Display +url=https://gitflic.ru/project/ogneyar/neopixel_amura +architectures=MIK32_Amur \ No newline at end of file diff --git a/libraries/NeoPixel/src/NeoPixel.cpp b/libraries/NeoPixel/src/NeoPixel.cpp new file mode 100644 index 0000000..c753860 --- /dev/null +++ b/libraries/NeoPixel/src/NeoPixel.cpp @@ -0,0 +1,366 @@ +#include "NeoPixel.h" + +NeoPixel::NeoPixel(uint16_t n, int16_t p, neoPixelType t) + : begun(false), brightness(0), pixels(NULL), endTime(0) { + updateType(t); + updateLength(n); + setPin(p); +} + +NeoPixel::NeoPixel() + : is800KHz(true), begun(false), numLEDs(0), numBytes(0), pin(-1), brightness(0), + pixels(NULL), rOffset(1), gOffset(0), bOffset(2), wOffset(1), endTime(0) { +} + +NeoPixel::~NeoPixel() { + free(pixels); + if (pin >= 0) + pinMode(pin, INPUT); +} + +void NeoPixel::begin(void) { + if (pin >= 0) { + pinMode(pin, OUTPUT); + digitalWrite(pin, LOW); + } + begun = true; +} + +void NeoPixel::updateLength(uint16_t n) { + free(pixels); // Free existing data (if any) + + // Allocate new data -- note: ALL PIXELS ARE CLEARED + numBytes = n * ((wOffset == rOffset) ? 3 : 4); + if ((pixels = (uint8_t *)malloc(numBytes))) { + memset(pixels, 0, numBytes); + numLEDs = n; + } else { + numLEDs = numBytes = 0; + } +} + +void NeoPixel::updateType(neoPixelType t) { + bool oldThreeBytesPerPixel = (wOffset == rOffset); // false if RGBW + + wOffset = (t >> 6) & 0b11; // See notes in header file + rOffset = (t >> 4) & 0b11; // regarding R/G/B/W offsets + gOffset = (t >> 2) & 0b11; + bOffset = t & 0b11; + is800KHz = (t < 256); // 400 KHz flag is 1<<8 + + if (pixels) { + bool newThreeBytesPerPixel = (wOffset == rOffset); + if (newThreeBytesPerPixel != oldThreeBytesPerPixel) + updateLength(numLEDs); + } +} + +static void __attribute__((noinline, section(".ram_text"))) mik32Show(GPIO_TypeDef* m_port, uint32_t m_pin, uint8_t* pixels, uint32_t numBytes, bool is800KHz) { + // not support 400khz + if (!is800KHz) return; + + volatile uint32_t* set = &m_port->SET; + volatile uint32_t* clr = &m_port->CLEAR; + + uint8_t* ptr = pixels; + uint8_t* end = ptr + numBytes; + uint8_t p = *ptr++; + uint8_t bitMask = 0x80; + + noInterrupts(); + + while (1) { + if (p & bitMask) { // ONE + // High 800ns - 25,6 tick + *set = m_pin; + __asm volatile ( + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop; nop;" + ); + + // Low 450ns - 14,4 tick + *clr = m_pin; + __asm volatile ( + "nop; nop; nop; nop; nop;" + ); + } else { // ZERO + // High 400ns - 12,8 tick + *set = m_pin; + __asm volatile ( + "nop; nop; nop; nop; nop; nop;" + ); + + // Low 850ns - 27,2 tick + *clr = m_pin; + __asm volatile ( + "nop; nop; nop; nop; nop; nop; nop; nop;" + "nop; nop; nop;" + ); + } + + if (bitMask >>= 1) { + // Move on to the next pixel + } + else { + if (ptr >= end) { + break; + } + p = *ptr++; + bitMask = 0x80; + } + } + interrupts(); +} + +void NeoPixel::show(void) { + + if (!pixels) + return; + + while (!canShow()) + ; + mik32Show(gpioPort, gpioPin, pixels, numBytes, is800KHz); + endTime = micros(); // Save EOD time for latch on next call +} + +void NeoPixel::setPin(int16_t p) { + if (begun && (pin >= 0)) + pinMode(pin, INPUT); // Disable existing out pin + pin = p; + if (begun) { + pinMode(p, OUTPUT); + digitalWrite(p, LOW); + } + + gpioPort = digitalPinToPort(pin); + gpioPin = digitalPinToBitMask(pin); +} + +void NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, + uint8_t b) { + if (n < numLEDs) { + if (brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + uint8_t *p; + if (wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = 0; // But only R,G,B passed -- set W to 0 + } + p[rOffset] = r; // R,G,B always stored + p[gOffset] = g; + p[bOffset] = b; + } +} + +void NeoPixel::setPixelColor(uint16_t n, uint8_t r, uint8_t g, + uint8_t b, uint8_t w) { + + if (n < numLEDs) { + if (brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + w = (w * brightness) >> 8; + } + uint8_t *p; + if (wOffset == rOffset) { // Is an RGB-type strip + p = &pixels[n * 3]; // 3 bytes per pixel (ignore W) + } else { // Is a WRGB-type strip + p = &pixels[n * 4]; // 4 bytes per pixel + p[wOffset] = w; // Store W + } + p[rOffset] = r; // Store R,G,B + p[gOffset] = g; + p[bOffset] = b; + } +} + +void NeoPixel::setPixelColor(uint16_t n, uint32_t c) { + if (n < numLEDs) { + uint8_t *p, r = (uint8_t)(c >> 16), g = (uint8_t)(c >> 8), b = (uint8_t)c; + if (brightness) { // See notes in setBrightness() + r = (r * brightness) >> 8; + g = (g * brightness) >> 8; + b = (b * brightness) >> 8; + } + if (wOffset == rOffset) { + p = &pixels[n * 3]; + } else { + p = &pixels[n * 4]; + uint8_t w = (uint8_t)(c >> 24); + p[wOffset] = brightness ? ((w * brightness) >> 8) : w; + } + p[rOffset] = r; + p[gOffset] = g; + p[bOffset] = b; + } +} + +void NeoPixel::fill(uint32_t c, uint16_t first, uint16_t count) { + uint16_t i, end; + + if (first >= numLEDs) { + return; // If first LED is past end of strip, nothing to do + } + + // Calculate the index ONE AFTER the last pixel to fill + if (count == 0) { + // Fill to end of strip + end = numLEDs; + } else { + // Ensure that the loop won't go past the last pixel + end = first + count; + if (end > numLEDs) + end = numLEDs; + } + + for (i = first; i < end; i++) { + this->setPixelColor(i, c); + } +} + +uint32_t NeoPixel::ColorHSV(uint16_t hue, uint8_t sat, uint8_t val) { + + uint8_t r, g, b; + + hue = (hue * 1530L + 32768) / 65536; + + if (hue < 510) { // Red to Green-1 + b = 0; + if (hue < 255) { // Red to Yellow-1 + r = 255; + g = hue; // g = 0 to 254 + } else { // Yellow to Green-1 + r = 510 - hue; // r = 255 to 1 + g = 255; + } + } else if (hue < 1020) { // Green to Blue-1 + r = 0; + if (hue < 765) { // Green to Cyan-1 + g = 255; + b = hue - 510; // b = 0 to 254 + } else { // Cyan to Blue-1 + g = 1020 - hue; // g = 255 to 1 + b = 255; + } + } else if (hue < 1530) { // Blue to Red-1 + g = 0; + if (hue < 1275) { // Blue to Magenta-1 + r = hue - 1020; // r = 0 to 254 + b = 255; + } else { // Magenta to Red-1 + r = 255; + b = 1530 - hue; // b = 255 to 1 + } + } else { // Last 0.5 Red (quicker than % operator) + r = 255; + g = b = 0; + } + + // Apply saturation and value to R,G,B, pack into 32-bit result: + uint32_t v1 = 1 + val; // 1 to 256; allows >>8 instead of /255 + uint16_t s1 = 1 + sat; // 1 to 256; same reason + uint8_t s2 = 255 - sat; // 255 to 0 + return ((((((r * s1) >> 8) + s2) * v1) & 0xff00) << 8) | + (((((g * s1) >> 8) + s2) * v1) & 0xff00) | + (((((b * s1) >> 8) + s2) * v1) >> 8); +} + + +uint32_t NeoPixel::getPixelColor(uint16_t n) const { + if (n >= numLEDs) + return 0; // Out of bounds, return no color. + + uint8_t *p; + + if (wOffset == rOffset) { // Is RGB-type device + p = &pixels[n * 3]; + if (brightness) { + + return (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ((uint32_t)(p[bOffset] << 8) / brightness); + } else { + // No brightness adjustment has been made -- return 'raw' color + return ((uint32_t)p[rOffset] << 16) | ((uint32_t)p[gOffset] << 8) | + (uint32_t)p[bOffset]; + } + } else { // Is RGBW-type device + p = &pixels[n * 4]; + if (brightness) { // Return scaled color + return (((uint32_t)(p[wOffset] << 8) / brightness) << 24) | + (((uint32_t)(p[rOffset] << 8) / brightness) << 16) | + (((uint32_t)(p[gOffset] << 8) / brightness) << 8) | + ((uint32_t)(p[bOffset] << 8) / brightness); + } else { // Return raw color + return ((uint32_t)p[wOffset] << 24) | ((uint32_t)p[rOffset] << 16) | + ((uint32_t)p[gOffset] << 8) | (uint32_t)p[bOffset]; + } + } +} + +void NeoPixel::setBrightness(uint8_t b) { + + uint8_t newBrightness = b + 1; + if (newBrightness != brightness) { + + uint8_t c, *ptr = pixels, + oldBrightness = brightness - 1; // De-wrap old brightness value + uint16_t scale; + if (oldBrightness == 0) + scale = 0; // Avoid /0 + else if (b == 255) + scale = 65535 / oldBrightness; + else + scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness; + for (uint16_t i = 0; i < numBytes; i++) { + c = *ptr; + *ptr++ = (c * scale) >> 8; + } + brightness = newBrightness; + } +} + +uint8_t NeoPixel::getBrightness(void) const { return brightness - 1; } + +void NeoPixel::clear(void) { memset(pixels, 0, numBytes); } + +uint32_t NeoPixel::gamma32(uint32_t x) { + uint8_t *y = (uint8_t *)&x; + + for (uint8_t i = 0; i < 4; i++) + y[i] = gamma8(y[i]); + return x; // Packed 32-bit return +} + +void NeoPixel::rainbow(uint16_t first_hue, int8_t reps, + uint8_t saturation, uint8_t brightness, bool gammify) { + for (uint16_t i=0; i + +// RGB NeoPixel permutations; white and red offsets are always same +// Offset: W R G B +#define NEO_RGB ((0 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B +#define NEO_RBG ((0 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G +#define NEO_GRB ((1 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B +#define NEO_GBR ((2 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R +#define NEO_BRG ((1 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G +#define NEO_BGR ((2 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R + +// RGBW NeoPixel permutations; all 4 offsets are distinct +// Offset: W R G B +#define NEO_WRGB ((0 << 6) | (1 << 4) | (2 << 2) | (3)) ///< Transmit as W,R,G,B +#define NEO_WRBG ((0 << 6) | (1 << 4) | (3 << 2) | (2)) ///< Transmit as W,R,B,G +#define NEO_WGRB ((0 << 6) | (2 << 4) | (1 << 2) | (3)) ///< Transmit as W,G,R,B +#define NEO_WGBR ((0 << 6) | (3 << 4) | (1 << 2) | (2)) ///< Transmit as W,G,B,R +#define NEO_WBRG ((0 << 6) | (2 << 4) | (3 << 2) | (1)) ///< Transmit as W,B,R,G +#define NEO_WBGR ((0 << 6) | (3 << 4) | (2 << 2) | (1)) ///< Transmit as W,B,G,R + +#define NEO_RWGB ((1 << 6) | (0 << 4) | (2 << 2) | (3)) ///< Transmit as R,W,G,B +#define NEO_RWBG ((1 << 6) | (0 << 4) | (3 << 2) | (2)) ///< Transmit as R,W,B,G +#define NEO_RGWB ((2 << 6) | (0 << 4) | (1 << 2) | (3)) ///< Transmit as R,G,W,B +#define NEO_RGBW ((3 << 6) | (0 << 4) | (1 << 2) | (2)) ///< Transmit as R,G,B,W +#define NEO_RBWG ((2 << 6) | (0 << 4) | (3 << 2) | (1)) ///< Transmit as R,B,W,G +#define NEO_RBGW ((3 << 6) | (0 << 4) | (2 << 2) | (1)) ///< Transmit as R,B,G,W + +#define NEO_GWRB ((1 << 6) | (2 << 4) | (0 << 2) | (3)) ///< Transmit as G,W,R,B +#define NEO_GWBR ((1 << 6) | (3 << 4) | (0 << 2) | (2)) ///< Transmit as G,W,B,R +#define NEO_GRWB ((2 << 6) | (1 << 4) | (0 << 2) | (3)) ///< Transmit as G,R,W,B +#define NEO_GRBW ((3 << 6) | (1 << 4) | (0 << 2) | (2)) ///< Transmit as G,R,B,W +#define NEO_GBWR ((2 << 6) | (3 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,W,R +#define NEO_GBRW ((3 << 6) | (2 << 4) | (0 << 2) | (1)) ///< Transmit as G,B,R,W + +#define NEO_BWRG ((1 << 6) | (2 << 4) | (3 << 2) | (0)) ///< Transmit as B,W,R,G +#define NEO_BWGR ((1 << 6) | (3 << 4) | (2 << 2) | (0)) ///< Transmit as B,W,G,R +#define NEO_BRWG ((2 << 6) | (1 << 4) | (3 << 2) | (0)) ///< Transmit as B,R,W,G +#define NEO_BRGW ((3 << 6) | (1 << 4) | (2 << 2) | (0)) ///< Transmit as B,R,G,W +#define NEO_BGWR ((2 << 6) | (3 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,W,R +#define NEO_BGRW ((3 << 6) | (2 << 4) | (1 << 2) | (0)) ///< Transmit as B,G,R,W + + +#define NEO_KHZ400 0x0100 ///< 400 KHz data transmission +#define NEO_KHZ800 0x0000 ///< 800 KHz data transmission + + +typedef uint16_t neoPixelType; ///< 3rd arg to NeoPixel constructor + + +static const uint8_t PROGMEM _NeoPixelSineTable[256] = { + 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, + 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, + 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, + 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, + 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, + 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232, + 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198, + 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155, + 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109, + 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65, + 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29, + 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6, + 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11, + 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37, + 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, + 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, + 124}; + + +static const uint8_t PROGMEM _NeoPixelGammaTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, + 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, + 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102, + 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125, + 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152, + 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, + 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215, + 218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252, + 255}; + + + +class NeoPixel { + +public: + // Constructor: number of LEDs, pin number, LED type + NeoPixel(uint16_t n, int16_t pin = 6, + neoPixelType type = NEO_GRB + NEO_KHZ800); + NeoPixel(void); + ~NeoPixel(); + + void begin(void); + void show(void); + void setPin(int16_t p); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b); + void setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w); + void setPixelColor(uint16_t n, uint32_t c); + void fill(uint32_t c = 0, uint16_t first = 0, uint16_t count = 0); + void setBrightness(uint8_t); + void clear(void); + void updateLength(uint16_t n); + void updateType(neoPixelType t); + + bool canShow(void) { + uint32_t now = micros(); + if (endTime > now) { + endTime = now; + } + return (now - endTime) >= 300L; + } + + uint8_t *getPixels(void) const { return pixels; }; + uint8_t getBrightness(void) const; + + int16_t getPin(void) const { return pin; }; + + uint16_t numPixels(void) const { return numLEDs; } + uint32_t getPixelColor(uint16_t n) const; + + static uint8_t sine8(uint8_t x) { + return pgm_read_byte(&_NeoPixelSineTable[x]); // 0-255 in, 0-255 out + } + + static uint8_t gamma8(uint8_t x) { + return pgm_read_byte(&_NeoPixelGammaTable[x]); // 0-255 in, 0-255 out + } + + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b) { + return ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { + return ((uint32_t)w << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + } + static uint32_t ColorHSV(uint16_t hue, uint8_t sat = 255, uint8_t val = 255); + + static uint32_t gamma32(uint32_t x); + + void rainbow(uint16_t first_hue = 0, int8_t reps = 1, + uint8_t saturation = 255, uint8_t brightness = 255, + bool gammify = true); + + static neoPixelType str2order(const char *v); + + +protected: + bool is800KHz; ///< true if 800 KHz pixels + bool begun; ///< true if begin() previously called + uint16_t numLEDs; ///< Number of RGB LEDs in strip + uint16_t numBytes; ///< Size of 'pixels' buffer below + int16_t pin; ///< Output pin number (-1 if not yet set) + uint8_t brightness; ///< Strip brightness 0-255 (stored as +1) + uint8_t *pixels; ///< Holds LED color values (3 or 4 bytes each) + uint8_t rOffset; ///< Red index within each 3- or 4-byte pixel + uint8_t gOffset; ///< Index of green byte + uint8_t bOffset; ///< Index of blue byte + uint8_t wOffset; ///< Index of white (==rOffset if no white) + uint32_t endTime; ///< Latch timing reference + GPIO_TypeDef *gpioPort; ///< Output GPIO PORT + uint32_t gpioPin; ///< Output GPIO PIN +}; + +#endif // NEOPIXEL_H diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 7a28066..ad30a09 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -2,10 +2,11 @@ #include "mik32_hal_spi.h" SPI_HandleTypeDef hspi; + bool newConfig = false; bool isInited = false; uint32_t currentSpeed = 0; -int8_t currentDataOrder = -1; +int8_t currentDataOrder = MSBFIRST; int8_t currentDataMode = -1; static uint8_t reverse_bits(uint8_t byte); @@ -31,16 +32,10 @@ void SPISettings::spiUpdateSettings(uint32_t speedMaximum, uint8_t dataOrder, ui } // if break didn't call in cycle, it will be the greatest divRegVal (and divider) - // update config - hspi.Instance = SPI_1; - hspi.Init.SPI_Mode = HAL_SPI_MODE_MASTER; // only master mode + // update params in struct hspi.Init.CLKPhase = dataMode & 0b00000001; hspi.Init.CLKPolarity = (dataMode & 0b00000010)>>1; - hspi.Init.ThresholdTX = 4; hspi.Init.BaudRateDiv = divRegVal; - hspi.Init.Decoder = SPI_DECODER_NONE; - hspi.Init.ManualCS = SPI_MANUALCS_ON; - hspi.Init.ChipSelect = SPI_CS_NONE; currentSpeed = speedMaximum; currentDataOrder = dataOrder; @@ -50,9 +45,6 @@ void SPISettings::spiUpdateSettings(uint32_t speedMaximum, uint8_t dataOrder, ui } // ------------------------------------------------------------------ // -// pins shift in gpio register -#define PIN_1_3_GPIO_S 3 -#define PIN_1_4_GPIO_S 4 SPIClass SPI; bool SPIClass::spiInUse = false; @@ -61,42 +53,19 @@ uint8_t SPIClass::interruptMask = 0; void SPIClass::begin() { - // there is a seller on pin 1.6 which replace D10 from spi NSS pin 1.3 to pin 1.4, - // because spi needs pin 1.3 for correct work - - // replace config from 1.3 to 1.4 - uint8_t config = ((PAD_CONFIG->PORT_1_CFG) & (0b11<<(2*PIN_1_3_GPIO_S))) >> (2*PIN_1_3_GPIO_S); - if (config == 0) // common gpio - { - // pin direction - uint8_t direction = ((GPIO_1->DIRECTION_IN) & (1<> PIN_1_3_GPIO_S; - if (direction == 1) // input - GPIO_1->DIRECTION_IN |= (1<DIRECTION_OUT |= (1<PORT_1_PUPD) & (0b11<<(2*PIN_1_3_GPIO_S))) >> (2*PIN_1_3_GPIO_S); - PAD_CONFIG ->PORT_1_PUPD &= (~(0b11<<(2*PIN_1_4_GPIO_S))); // clear - PAD_CONFIG ->PORT_1_PUPD |= ((pupd_1_3&0b11)<<(2*PIN_1_4_GPIO_S)); // set new - - //current state - uint8_t state1_3 = ((GPIO_1->OUTPUT_) & (1<> PIN_1_3_GPIO_S; - GPIO_1->OUTPUT_ &= (~(0b1<OUTPUT_ |= ((state1_3&0b1)<>1; spiInUse = true; } @@ -113,31 +82,9 @@ void SPIClass::end() { HAL_GPIO_PinConfig(GPIO_1, (HAL_PinsTypeDef)(GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2), HAL_GPIO_MODE_GPIO_INPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); - } + } - // return D10 with common gpio config to pin 1.3 - PAD_CONFIG->PORT_1_CFG &= (~(0b11<<(2*PIN_1_3_GPIO_S))); // set config to 0 - common gpio mode - - // pin direction - uint8_t direction = ((GPIO_1->DIRECTION_IN) & (1<> PIN_1_4_GPIO_S; - if (direction == 1) // input - GPIO_1->DIRECTION_IN |= (1<DIRECTION_OUT |= (1<PORT_1_PUPD) & (0b11<<(2*PIN_1_4_GPIO_S))) >> (2*PIN_1_4_GPIO_S); - PAD_CONFIG ->PORT_1_PUPD &= (~(0b11<<(2*PIN_1_3_GPIO_S))); // clear - PAD_CONFIG ->PORT_1_PUPD |= ((pupd_1_4&0b11)<<(2*PIN_1_3_GPIO_S)); // set new - - // current state - uint8_t state1_4 = ((GPIO_1->OUTPUT_) & (1<> PIN_1_4_GPIO_S; - GPIO_1->OUTPUT_ &= (~(0b1<OUTPUT_ |= ((state1_4&0b1)<CONFIG; - HAL_SPI_Disable(&hspi); - hspi.Init.CLKPhase = dataMode & 0b00000001; - hspi.Init.CLKPolarity = (dataMode & 0b00000010)>>1; - config &= ~((1 << SPI_CONFIG_CLK_PH_S) | (1 << SPI_CONFIG_CLK_POL_S)); // clear - config |= ((hspi.Init.CLKPhase << SPI_CONFIG_CLK_PH_S) | - (hspi.Init.CLKPolarity << SPI_CONFIG_CLK_POL_S)); // set new - hspi.Instance->CONFIG = config; - HAL_SPI_Enable(&hspi); + if (spiInUse) + { + hspi.Init.CLKPhase = (dataMode&0b00000001); + hspi.Init.CLKPolarity = (dataMode&0b00000010)>>1; + HAL_SPI_Set_Clock_Mode(&hspi); + currentDataMode = dataMode; + } + else + ErrorMsgHandler("SPI.setDataMode():SPI.begin() need to be called first"); } void SPIClass::setClockDivider(uint8_t clockDiv) { - // if divider is in valid range - if ((clockDiv >= SPI_CLOCK_DIV4) && (clockDiv <= SPI_CLOCK_DIV256)) + if (spiInUse) { - uint32_t config = hspi.Instance->CONFIG; - HAL_SPI_Disable(&hspi); - hspi.Init.BaudRateDiv = clockDiv; - config &= ~(0b111 << SPI_CONFIG_BAUD_RATE_DIV_S); // clear - config |= (hspi.Init.BaudRateDiv << SPI_CONFIG_BAUD_RATE_DIV_S); // set new - hspi.Instance->CONFIG = config; - HAL_SPI_Enable(&hspi); + // if divider is valid + if ((clockDiv == SPI_CLOCK_DIV4) || (clockDiv == SPI_CLOCK_DIV8) || + (clockDiv == SPI_CLOCK_DIV16) || (clockDiv == SPI_CLOCK_DIV32) || + (clockDiv == SPI_CLOCK_DIV64) || (clockDiv == SPI_CLOCK_DIV128) || + (clockDiv == SPI_CLOCK_DIV256)) + { + hspi.Init.BaudRateDiv = clockDiv; + currentSpeed = F_CPU >> (clockDiv+1); + HAL_SPI_Set_Clock_Divider(&hspi); + } + else + ErrorMsgHandler("SPI.setClockDivider(): Invalid clock devider"); } + else + ErrorMsgHandler("SPI.setClockDivider():SPI.begin() need to be called first"); } static uint8_t reverse_bits(uint8_t byte) diff --git a/libraries/Servo/LICENSE.txt b/libraries/Servo/LICENSE.txt new file mode 100644 index 0000000..8000a6f --- /dev/null +++ b/libraries/Servo/LICENSE.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random + Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libraries/Servo/README.md b/libraries/Servo/README.md new file mode 100644 index 0000000..78de539 --- /dev/null +++ b/libraries/Servo/README.md @@ -0,0 +1,2 @@ + +## Servo - это библиотека Servo для Амура (МК от Микрон) в ArduinoIDE diff --git a/libraries/Servo/examples/Broom/Broom.ino b/libraries/Servo/examples/Broom/Broom.ino new file mode 100644 index 0000000..511e2f9 --- /dev/null +++ b/libraries/Servo/examples/Broom/Broom.ino @@ -0,0 +1,27 @@ + +/* + "Broom" Servo + This example demonstrates the servo drive movement + with a smooth stop in both directions +*/ + +#include + +Servo servo; // create an Servo class instance to control the servo + +int pos = 0; // variable for storing servo position + +void setup() { + servo.attach(8); // connecting the servo drive to digital pin 8 (D8) +} + +void loop() { + for (pos = 0; pos <= 180; pos += 1) { // change degrees from 0 to 180 + servo.write(pos); // command to servo drive - go to position of variable "pos" + delay(15); // waiting for the servo to get there + } + for (pos = 180; pos >= 0; pos -= 1) { // change degrees from 180 to 0 + servo.write(pos); // command to servo drive - go to position of variable "pos" + delay(15); // waiting for the servo to get there + } +} diff --git a/libraries/Servo/examples/Jerks/Jerks.ino b/libraries/Servo/examples/Jerks/Jerks.ino new file mode 100644 index 0000000..3e5727d --- /dev/null +++ b/libraries/Servo/examples/Jerks/Jerks.ino @@ -0,0 +1,25 @@ + +/* + "Jerks" + This example demonstrates the servo drive movement + with a sharp stop in both directions +*/ + +#include + +Servo servo; // create an Servo class instance to control the servo + +void setup() { + servo.attach(8); // connecting the servo drive to digital pin 8 (D8) +} + +void loop() { + servo.write(0); // command to the servo drive - go to the 0 degree position (clockwise movement) + delay(1000); // wait + servo.write(90); // stop movement + delay(1000); // wait + servo.write(180); // command to the servo drive - move to the 180 degree position (counterclockwise movement) + delay(1000); // wait + servo.write(90); // stop movement + delay(1000); // wait +} diff --git a/libraries/Servo/examples/Resist/Resist.ino b/libraries/Servo/examples/Resist/Resist.ino new file mode 100644 index 0000000..2e25383 --- /dev/null +++ b/libraries/Servo/examples/Resist/Resist.ino @@ -0,0 +1,24 @@ + +/* + "Resist" + This example demonstrates the servo drive movement + depending on the value on the potentiometer +*/ + +#include + +Servo servo; // create an Servo class instance to control the servo + +int potpin = A0; // analog pin used to connect a potentiometer +int val; // variable for reading the analog output value + +void setup() { + servo.attach(8); // connecting the servo drive to digital pin 8 (D8) +} + +void loop() { + val = analogRead(potpin); // reading the potentiometer value (value from 0 to 4096) + val = map(val, 0, 4096, 0, 180); // change scale for use with servo (value from 0 to 180) + servo.write(val); // setting the servo position according to the value + delay(15); // waiting for the servo to get there +} diff --git a/libraries/Servo/keywords.txt b/libraries/Servo/keywords.txt new file mode 100644 index 0000000..8ea1871 --- /dev/null +++ b/libraries/Servo/keywords.txt @@ -0,0 +1,23 @@ +####################################### +# Syntax Coloring Map Servo +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Servo KEYWORD1 Servo + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +attach KEYWORD2 +detach KEYWORD2 +write KEYWORD2 +read KEYWORD2 +writeMicroseconds KEYWORD2 +readMicroseconds KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/Servo/library.properties b/libraries/Servo/library.properties new file mode 100644 index 0000000..bf96c1c --- /dev/null +++ b/libraries/Servo/library.properties @@ -0,0 +1,10 @@ +name=Servo +version=0.0.1 +author=Arduino +maintainer=Ogneyar +sentence=Allows the ELBEAR ACE-UNO board to control various servo motors. +paragraph=This library can control the servo.
It uses a 16-bit timer: timer16_2. +category=Device Control +url=https://gitflic.ru/project/ogneyar/servo_amura +repository = https://gitflic.ru/project/ogneyar/servo_amura.git +architectures=MIK32_Amur \ No newline at end of file diff --git a/libraries/Servo/src/Servo.cpp b/libraries/Servo/src/Servo.cpp new file mode 100644 index 0000000..1911da0 --- /dev/null +++ b/libraries/Servo/src/Servo.cpp @@ -0,0 +1,257 @@ + +#include +#include "pins_arduino.h" + +#include "mik32_hal_timer16.h" +#include "mik32_hal_irq.h" +#include "wiring_LL.h" + +#include "Servo.h" + + +static servo_t servos[MAX_SERVOS]; // static array of servo structures +static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval) +uint8_t servoCount = 0; // the total number of attached servos + +uint16_t Period = 0xFFFF; +uint16_t Compare = 0xFFFF; +uint8_t prescaler = TIMER16_PRESCALER_16; + +#define usToTicks(_us) ((clockCyclesPerMicrosecond() * _us) / ( (1 << prescaler) )) // converts microseconds to ticks (TIMERx_PRESCALER_16) +#define ticksToUs(_ticks) (((unsigned) _ticks * ( (1 << prescaler) )) / clockCyclesPerMicrosecond()) // converts from ticks back to microseconds + +#define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in us for this servo +#define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in us for this servo + +#define TRIM_DURATION 5 // compensation ticks to trim adjust for digitalWrite delays + +// convenience macros +#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / SERVOS_PER_TIMER)) // returns the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % SERVOS_PER_TIMER) // returns the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*SERVOS_PER_TIMER) + _channel) // macro to access servo index by timer and channel +#define SERVO(_timer,_channel) (servos[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel + +// timer handler +Timer16_HandleTypeDef htimer16; + +void Timer16_2_Init() +{ + htimer16.Instance = TIMER16_2; + htimer16.Clock.Source = TIMER16_SOURCE_INTERNAL_SYSTEM; + // prescaler depends on required frequency + htimer16.Clock.Prescaler = prescaler; + htimer16.CountMode = TIMER16_COUNTMODE_INTERNAL; + htimer16.ActiveEdge = TIMER16_ACTIVEEDGE_RISING; + htimer16.Preload = TIMER16_PRELOAD_AFTERWRITE; + htimer16.Trigger.Source = TIMER16_TRIGGER_TIM2_GPIO2_3; + // timer triggers by software + htimer16.Trigger.ActiveEdge = TIMER16_TRIGGER_ACTIVEEDGE_SOFTWARE; + htimer16.Trigger.TimeOut = TIMER16_TIMEOUT_DISABLE; + htimer16.Filter.ExternalClock = TIMER16_FILTER_NONE; + htimer16.Filter.Trigger = TIMER16_FILTER_NONE; + htimer16.Waveform.Enable = TIMER16_WAVEFORM_GENERATION_ENABLE; + htimer16.Waveform.Polarity = TIMER16_WAVEFORM_POLARITY_NONINVERTED; + htimer16.EncoderMode = TIMER16_ENCODER_DISABLE; + HAL_Timer16_Init(&htimer16); +} + + +/************ static functions common to all instances ***********************/ + +// irq handler +extern "C" void __attribute__((optimize("O3"))) servo_interrupt_handler(void) +{ + if (TIM16_GET_ARRM_INT_STATUS(htimer16)) + { + timer16_Sequence_t timer = _timer1; + + if( currentServoIndex[timer] < 0 ) + Compare = 0; + else{ + if( SERVO_INDEX(timer, currentServoIndex[timer]) < servoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive == true ) + digitalWrite( SERVO(timer,currentServoIndex[timer]).Pin.nbr, LOW ); // pulse this channel low if activated + } + + currentServoIndex[timer]++; // increment to the next channel + if( SERVO_INDEX(timer, currentServoIndex[timer]) < servoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) { + Period = Compare + SERVO(timer, currentServoIndex[timer]).ticks; + if(SERVO(timer, currentServoIndex[timer]).Pin.isActive == true) // check if activated + digitalWrite( SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH ); // it's an active channel so pulse it high + } + else { + // finished all channels so wait for the refresh period to expire before starting over + if( ((unsigned)Compare) + 4 < usToTicks(REFRESH_INTERVAL) ) // allow a few ticks to ensure the next OCR1A not missed + Period = (unsigned int)usToTicks(REFRESH_INTERVAL); + else + Period = Compare + 4; // at least REFRESH_INTERVAL has elapsed + currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + } + HAL_Timer16_Counter_Start_IT(&htimer16, Period); + } + + // reset timer interrupt flags + TIM16_CLEAR_INT_MASK(htimer16, 0xFFFFFFFF); +} + +// init isr +static void initISR(timer16_Sequence_t timer) +{ + if (timer == _timer1) + { + Timer16_2_Init(); + HAL_EPIC_MaskLevelSet(HAL_EPIC_TIMER16_2_MASK); + HAL_Timer16_Counter_Start_IT(&htimer16, 0xFFFF); + } +} + +// deinit isr +static void finISR(timer16_Sequence_t timer) +{ + if (timer == _timer1) + { + TIM16_DISABLE(htimer16); + TIM16_DISABLE_INT_BY_MASK(htimer16, TIMER16_IER_ARROKIE_M | TIMER16_IER_CMPOKIE_M | TIMER16_IER_ARRMIE_M | TIMER16_IER_CMPMIE_M); + EPIC_LEVEL_CLEAR_BY_MASK(HAL_EPIC_TIMER16_1_MASK); + } +} + +// is timer active? +static bool isTimerActive(timer16_Sequence_t timer) +{ + // returns true if any servo is active on this timer + for(uint8_t channel = 0; channel < SERVOS_PER_TIMER; channel++) { + if(SERVO(timer, channel).Pin.isActive == true) + return true; + } + return false; +} + +/****************** end of static functions ******************************/ + +// -------------------------------------------------- +Servo::Servo() +{ + if (servoCount < MAX_SERVOS) { + this->servoIndex = servoCount++; // assign a servo index to this instance + servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values + } else { + this->servoIndex = INVALID_SERVO; // too many servos + } +}; +// -------------------------------------------------- + + +// -------------------------------------------------- +uint8_t Servo::attach(int pin) +{ + return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} +// -------------------------------------------------- + + +// -------------------------------------------------- +uint8_t Servo::attach(int pin, int min, int max) +{ + timer16_Sequence_t timer; + + if (this->servoIndex < MAX_SERVOS) { + pinMode(pin, OUTPUT); // set servo pin to output + servos[this->servoIndex].Pin.nbr = pin; + // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 + this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 us + this->max = (MAX_PULSE_WIDTH - max)/4; + // initialize the timer if it has not already been initialized + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if (isTimerActive(timer) == false) { + initISR(timer); + } + servos[this->servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + } + + return this->servoIndex; +} +// -------------------------------------------------- + + +// -------------------------------------------------- +void Servo::detach() +{ + timer16_Sequence_t timer; + + servos[this->servoIndex].Pin.isActive = false; + timer = SERVO_INDEX_TO_TIMER(servoIndex); + if(isTimerActive(timer) == false) { + finISR(timer); + pinMode(servos[this->servoIndex].Pin.nbr, INPUT); // deinit pin + } +} +// -------------------------------------------------- + + +// -------------------------------------------------- +void Servo::write(int value) +{ + // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + if (value < MIN_PULSE_WIDTH) + { + if (value < 0) + value = 0; + else if (value > 180) + value = 180; + + value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); + } + writeMicroseconds(value); +} +// -------------------------------------------------- + + +// -------------------------------------------------- +void Servo::writeMicroseconds(int value) +{ + // calculate and store the values for the given channel + byte channel = this->servoIndex; + if( (channel < MAX_SERVOS) ) // ensure channel is valid + { + if (value < SERVO_MIN()) // ensure pulse width is valid + value = SERVO_MIN(); + else if (value > SERVO_MAX()) + value = SERVO_MAX(); + + value = value - TRIM_DURATION; + value = usToTicks(value); // convert to ticks after compensating for interrupt overhead + servos[channel].ticks = value; + } +} +// -------------------------------------------------- + + +// -------------------------------------------------- +int Servo::read() // return the value as degrees +{ + return map(readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); +} +// -------------------------------------------------- + + +// -------------------------------------------------- +int Servo::readMicroseconds() +{ + unsigned int pulsewidth; + if (this->servoIndex != INVALID_SERVO) + pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION; + else + pulsewidth = 0; + + return pulsewidth; +} +// -------------------------------------------------- + + +// -------------------------------------------------- +bool Servo::attached() +{ + return servos[this->servoIndex].Pin.isActive; +} +// -------------------------------------------------- + diff --git a/libraries/Servo/src/Servo.h b/libraries/Servo/src/Servo.h new file mode 100644 index 0000000..d6da3ff --- /dev/null +++ b/libraries/Servo/src/Servo.h @@ -0,0 +1,53 @@ + +#ifndef Servo_h +#define Servo_h + +#include + +#include "mik32_hal_timer16.h" +#include "mik32_hal_irq.h" + +typedef enum { + _timer1, // timer16_2 + _Nbr_16timers +} timer16_Sequence_t; + +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo +#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached +#define REFRESH_INTERVAL 20000 // minimum time to refresh servo in microseconds + +#define SERVOS_PER_TIMER 12 // the maximum number of servos controlled by one timer +#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER) + +#define INVALID_SERVO 255 // flag indicating an invalid servo index + +typedef struct { + uint8_t nbr :6 ; // a pin number from 0 to 63 + uint8_t isActive :1 ; // true if this channel is enabled, pin not pulsed if false +} ServoPin_t ; + +typedef struct { + ServoPin_t Pin; + volatile unsigned int ticks; +} servo_t; + +class Servo +{ +public: + Servo(); + uint8_t attach(int pin); // attach the given pin to the next free channel, sets pinMode, returns channel number or INVALID_SERVO if failure + uint8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes. + void detach(); + void write(int value); // if value is < 200 it's treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds + int read(); // returns current pulse width as an angle between 0 and 180 degrees + int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) + bool attached(); // return true if this servo is attached, otherwise false +private: + uint8_t servoIndex; // index into the channel data for this servo + int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH +}; + +#endif diff --git a/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino b/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino new file mode 100644 index 0000000..1663767 --- /dev/null +++ b/libraries/SoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino @@ -0,0 +1,47 @@ +/* + Software serial multiple serial test + + Receives from the hardware serial, sends to software serial. + Receives from software serial, sends to hardware serial. + + The circuit: + * RX is digital pin 2 (connect to TX of other device) + * TX is digital pin 6 (connect to RX of other device) + + Note: + Not all pins on the Elbear Ace-Uno support interrupts, + so only the following can be used for RX: + 2, 3, 4, 5, 8, 9 + + created back in the mists of time + modified 25 May 2012 + by Tom Igoe + based on Mikal Hart's example + + This example code is in the public domain. + + */ +#include + +SoftwareSerial mySerial(2, 6); // RX, TX + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(115200); + while (!Serial) ; // wait for serial port to connect. Needed for native USB port only + Serial.println("Goodnight moon!"); + + // set the data rate for the SoftwareSerial port + mySerial.begin(9600); + mySerial.println("Hello, world?"); +} + +void loop() { // run over and over + while (mySerial.available()) + Serial.write(mySerial.read()); + + while (Serial.available()) + mySerial.write(Serial.read()); + + delay(50); +} diff --git a/libraries/SoftwareSerial/examples/TwoPortReceive/TwoPortReceive.ino b/libraries/SoftwareSerial/examples/TwoPortReceive/TwoPortReceive.ino new file mode 100644 index 0000000..62a57cc --- /dev/null +++ b/libraries/SoftwareSerial/examples/TwoPortReceive/TwoPortReceive.ino @@ -0,0 +1,99 @@ +/* + Software serial multiple serial test + + Receives from the two software serial ports, + sends to the hardware serial port. + + In order to listen on a software port, you call port.listen(). + When using two software serial ports, you have to switch ports + by listen()ing on each one in turn. Pick a logical time to switch + ports, like the end of an expected transmission, or when the + buffer is empty. This example switches ports when there is nothing + more to read from a port + + The circuit: + Two devices which communicate serially are needed. + * First serial device's TX attached to digital pin 2(RX), RX to pin 6(TX) + * Second serial device's TX attached to digital pin 8(RX), RX to pin 10(TX) + + Note: + Not all pins on the Elbear Ace-Uno support interrupts, + so only the following can be used for RX: + 2, 3, 4, 5, 8, 9 + */ + +#include + +// software serial #1: RX = digital pin 2, TX = digital pin 6 +SoftwareSerial portOne(2, 6); +// software serial #2: RX = digital pin 8, TX = digital pin 10 +SoftwareSerial portTwo(8, 10); + +// active port is changed by user button pressing +volatile bool isPortOneActive = true; +void btn_pressed_callback(void) +{ + // change active port + if (isPortOneActive) + { + portTwo.listen(); + Serial.println("Port two is listening"); + isPortOneActive = false; + } + else + { + portOne.listen(); + Serial.println("Port one is listening"); + isPortOneActive = true; + } +} + +void setup() +{ + // Open serial communications and wait for port to open + Serial.begin(115200); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // attach interrupt to user button + attachInterrupt(digitalPinToInterrupt(BTN_BUILTIN), btn_pressed_callback, FALLING); + + // Start each software serial port + portOne.begin(57600); + portTwo.begin(57600); + + // First listen to port one + portOne.listen(); + isPortOneActive = true; + Serial.println("Port one is listening"); +} + +void loop() +{ + // check data from port one + if (portOne.available()) + { + Serial.println("Data from port one:"); + // while there is data coming in, read it + // and send to the hardware serial port + while (portOne.available() > 0) + Serial.write((char)portOne.read()); + + // blank line to separate data from the two ports + Serial.println(); + } + + // check data from port two + if (portTwo.available()) + { + Serial.println("Data from port two:"); + // while there is data coming in, read it + // and send to the hardware serial port + while (portTwo.available() > 0) + Serial.write((char)portTwo.read()); + + // blank line to separate data from the two ports + Serial.println(); + } +} diff --git a/libraries/SoftwareSerial/keywords.txt b/libraries/SoftwareSerial/keywords.txt new file mode 100644 index 0000000..aaea17c --- /dev/null +++ b/libraries/SoftwareSerial/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map for SoftwareSerial +# (formerly NewSoftSerial) +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +SoftwareSerial KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 +read KEYWORD2 +write KEYWORD2 +available KEYWORD2 +isListening KEYWORD2 +overflow KEYWORD2 +flush KEYWORD2 +listen KEYWORD2 +peek KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/libraries/SoftwareSerial/library.properties b/libraries/SoftwareSerial/library.properties new file mode 100644 index 0000000..61c635b --- /dev/null +++ b/libraries/SoftwareSerial/library.properties @@ -0,0 +1,10 @@ +name=SoftwareSerial +version=0.0.0 +author=Arduino +maintainer=Arduino +sentence=Enables serial communication on any digital pin. +paragraph=The SoftwareSerial library has been developed to allow serial communication on any digital pin of the board, using software to replicate the functionality of the hardware UART. It is possible to have multiple software serial ports with speeds up to 115200 bps. +category=Communication +url=http://www.arduino.cc/en/Reference/SoftwareSerial +architectures=MIK32_Amur + diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.cpp b/libraries/SoftwareSerial/src/SoftwareSerial.cpp new file mode 100644 index 0000000..0f312a2 --- /dev/null +++ b/libraries/SoftwareSerial/src/SoftwareSerial.cpp @@ -0,0 +1,361 @@ +/* +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The latest version of this library can always be found at +http://arduiniana.org. +*/ + +#include +#include +#include "wiring_LL.h" + +#define MAX_SOFT_SERIAL_SPEED 115200 +#define MIN_SOFT_SERIAL_SPEED 300 + +// Statics +// static variable is common to all class instances +SoftwareSerial *SoftwareSerial::active_object = 0; +uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; +volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; +volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; + +// Private methods + +// function placed in ram. Should not call other function placed in flash +void SoftwareSerial::tunedDelay(uint32_t delayTicks) // .ram_text +{ + if (delayTicks > 1) + { + uint64_t startTicks = SYSTICK_GET_TICKS(); + while ((SYSTICK_GET_TICKS() - startTicks) < delayTicks) + ; + } +} + +// This function sets the current object as the "listening" +// one and returns true if it replaces another +bool SoftwareSerial::listen() +{ + if (!_rx_delay_stopbit) + return false; + + if (active_object != this) + { + if (active_object) + active_object->stopListening(); + + _buffer_overflow = false; + _receive_buffer_head = _receive_buffer_tail = 0; + active_object = this; + + setRxIntMsk(true); + return true; + } + + return false; +} + +// Stop listening. Returns true if we were actually listening. +bool SoftwareSerial::stopListening() +{ + if (active_object == this) + { + setRxIntMsk(false); + active_object = NULL; + return true; + } + return false; +} + +// +// The receive routine called by the interrupt handler +// +void SoftwareSerial::recv() // .ram_text +{ + // If RX line is high, then we don't see any start bit + // so interrupt is probably not for us + bool rxState = (bool)GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask); + if (_inverse_logic ? rxState : !rxState) + { + // Disable further interrupts during reception, this prevents triggering another interrupt + // directly after we return, which can cause problems at higher baudrates. + setRxIntMsk(false); // __always_inline__ + + // Wait approximately 1/2 of a bit width to "center" the sample + if (_rx_delay_centering > 0) + tunedDelay(_rx_delay_centering); // .ram_text + + // Read each of the 8 bits + uint8_t data = 0; + for (uint8_t i=8; i > 0; --i) + { + tunedDelay(_rx_delay_intrabit); + data >>= 1; + if (GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask)) + data |= 0x80; + } + if (_inverse_logic) + data = ~data; + + // if buffer full, set the overflow flag and return + uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; + if (next != _receive_buffer_head) + { + // save new data in buffer: tail points to where byte goes + _receive_buffer[_receive_buffer_tail] = data; // save new byte + _receive_buffer_tail = next; + } + else + _buffer_overflow = true; + + // skip the stop bit + tunedDelay(_rx_delay_stopbit); + + // Re-enable interrupts when we're sure to be inside the stop bit + setRxIntMsk(true); + } + GPIO_IRQ_CLEAR_ALL(); +} + +uint8_t SoftwareSerial::rx_pin_read() +{ + return (uint8_t)GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask); +} + +// Interrupt handling + +/* static */ +void SoftwareSerial::handle_interrupt() // .ram_text +{ + if (active_object) + { + active_object->recv(); // .ram_text + } +} + +extern "C" void __attribute__((noinline, section(".ram_text"))) softSerial_interrupt_handler(void) +{ + SoftwareSerial::handle_interrupt(); +} + + +// +// Constructor +// +SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : + _rx_delay_centering(0), + _rx_delay_intrabit(0), + _rx_delay_stopbit(0), + _tx_delay(0), + _buffer_overflow(false), + _inverse_logic(inverse_logic) +{ + _transmitPin = transmitPin; + _receivePin = receivePin; +} + +// +// Destructor +// +SoftwareSerial::~SoftwareSerial() +{ + end(); +} + +void SoftwareSerial::setTX(uint8_t tx) +{ + // save pin and port info + _transmitBitMask = (uint16_t)digitalPinToBitMask(tx); + _transmitPortRegister = (uint32_t)digitalPinToPort(tx); + // set state before tx pin initialization to prevent false start bit + if (_inverse_logic) + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + else + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + // init pin as output + pinMode(tx, OUTPUT); +} + +void SoftwareSerial::setRX(uint8_t rx) +{ + // save pin, port and gpio_line info + _receiveBitMask = (uint16_t)digitalPinToBitMask(rx); + _receivePortRegister = (uint32_t)digitalPinToPort(rx); + _int_maskLine = digitalPinToGpioIntLine(_receivePin); + + // attach interrupt to rx pin if available + attachInterrupt(digitalPinToInterrupt(_receivePin), softSerial_interrupt_handler, _inverse_logic ? RISING : FALLING); + // turn on pull up for rx if logic is not inverse + if (!_inverse_logic) + { + uint8_t pinNumber = PIN_MASK_TO_PIN_NUMBER(_receiveBitMask); + if (((GPIO_TypeDef*)_receivePortRegister) == GPIO_0) + PIN_SET_PAD_CONFIG(PORT_0_PUPD, pinNumber, HAL_GPIO_PULL_UP); + else if (((GPIO_TypeDef*)_receivePortRegister) == GPIO_1) + PIN_SET_PAD_CONFIG(PORT_1_PUPD, pinNumber, HAL_GPIO_PULL_UP); + } + // turn off int line for while (it turning on in attachInterrupt()) + GPIO_IRQ_LINE_DISABLE(_int_maskLine); +} + +uint32_t SoftwareSerial::subtract_cap(uint32_t num, uint16_t sub) +{ + if (num > sub) + return num - sub; + else + return 1; +} + +// +// Public methods +// +void SoftwareSerial::begin(long speed) +{ + // delays are empirical values here + _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; + + // limit speed + if (speed > MAX_SOFT_SERIAL_SPEED) speed = MAX_SOFT_SERIAL_SPEED; + if (speed < MIN_SOFT_SERIAL_SPEED) speed = MIN_SOFT_SERIAL_SPEED; + + // Precalculate the various delays in number of ticks + uint32_t bit_delay = F_CPU / speed; + + // init tx + setTX(_transmitPin); + _tx_delay = subtract_cap(bit_delay, 35); // 1 bit delay while transmitting data + + // init rx only when we have a valid INT for this pin + if (digitalPinToInterrupt(_receivePin) != NOT_AN_INTERRUPT) + { + // set pin config + setRX(_receivePin); + + // We want to have a total delay of 1.5 bit time from start bit. Inside the loop, we already + // wait for 1 bit time, so here we wait for 0.5 bit time + _rx_delay_centering = subtract_cap(bit_delay / 2, 315); + + // 1 bit time + _rx_delay_intrabit = subtract_cap(bit_delay, 60); + + // This delay aims at 3/4 of a bit time, meaning the end of the delay will be at 1/4th of the stopbit. + // This allows some extra time for ISR cleanup, which makes 115200 baud at 16Mhz work more reliably + _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, 142); + } + else + ErrorMsgHandler("SoftwareSerial.begin(): Rx pin does not support interrupts, use different pin"); + + tunedDelay(_tx_delay); // if we were low this establishes the end + + listen(); +} + +void SoftwareSerial::setRxIntMsk(bool enable) +{ + if (enable) + GPIO_IRQ_LINE_ENABLE(_int_maskLine); + else + GPIO_IRQ_LINE_DISABLE(_int_maskLine); +} + +void SoftwareSerial::end() +{ + stopListening(); +} + +// function placed in ram. Should not call other function placed in flash +size_t SoftwareSerial::write(uint8_t byte) +{ + if (_tx_delay == 0) + return 0; + + if (_inverse_logic) + byte = ~byte; + + GLOBAL_IRQ_DISABLE(); // turn off interrupts for a clean txmit + + // Write the start bit + if (_inverse_logic) + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + else + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + tunedDelay(_tx_delay); // wait start bit + + // Write each of the 8 bits in LSB mode + for (uint8_t i = 8; i > 0; --i) + { + if (byte & 1) // choose bit + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + else + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + tunedDelay(_tx_delay); + byte >>= 1; + } + + // restore pin to natural state - stop bit + if (_inverse_logic) + GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + else + GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask); + + // enable interrupts + GLOBAL_IRQ_ENABLE(); + + tunedDelay(_tx_delay); // wait stop bit + + return 1; +} + +// Read data from buffer +int SoftwareSerial::read() +{ + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + uint8_t data = _receive_buffer[_receive_buffer_head]; // grab next byte + _receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF; + return data; +} + +int SoftwareSerial::available() +{ + if (!isListening()) + return 0; + + return ((unsigned int)(_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head)) % _SS_MAX_RX_BUFF; +} + +void SoftwareSerial::flush() +{ + // There is no tx buffering, simply return +} + +int SoftwareSerial::peek() +{ + if (!isListening()) + return -1; + + // Empty buffer? + if (_receive_buffer_head == _receive_buffer_tail) + return -1; + + // Read from "head" + return _receive_buffer[_receive_buffer_head]; +} \ No newline at end of file diff --git a/libraries/SoftwareSerial/src/SoftwareSerial.h b/libraries/SoftwareSerial/src/SoftwareSerial.h new file mode 100644 index 0000000..eea17bc --- /dev/null +++ b/libraries/SoftwareSerial/src/SoftwareSerial.h @@ -0,0 +1,109 @@ +/* +SoftwareSerial.h +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The latest version of this library can always be found at +http://arduiniana.org. +*/ + +#ifndef SoftwareSerial_h +#define SoftwareSerial_h + +#include +#include + +/****************************************************************************** +* Definitions +******************************************************************************/ + +#ifndef _SS_MAX_RX_BUFF +#define _SS_MAX_RX_BUFF 64 // RX buffer size +#endif + +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +#endif + +class SoftwareSerial : public Stream +{ +private: + // per object data + uint8_t _receivePin; + uint16_t _receiveBitMask; + uint32_t _receivePortRegister; + uint8_t _transmitPin; + uint16_t _transmitBitMask; + uint32_t _transmitPortRegister; + uint8_t _int_maskLine; + + // must never be 0! + uint32_t _rx_delay_centering; + uint32_t _rx_delay_intrabit; + uint32_t _rx_delay_stopbit; + uint32_t _tx_delay; + + uint16_t _buffer_overflow:1; + uint16_t _inverse_logic:1; + + // static data + static uint8_t _receive_buffer[_SS_MAX_RX_BUFF]; + static volatile uint8_t _receive_buffer_tail; + static volatile uint8_t _receive_buffer_head; + static SoftwareSerial *active_object; + + // private methods + void recv() __attribute__((noinline, section(".ram_text"))); + uint8_t rx_pin_read(); + void setTX(uint8_t transmitPin); + void setRX(uint8_t receivePin); + inline void setRxIntMsk(bool enable) __attribute__((__always_inline__)); + + // Return num - sub, or 1 if the result would be < 1 + static uint32_t subtract_cap(uint32_t num, uint16_t sub); + + // private static method for timing + static void tunedDelay(uint32_t delayTicks) __attribute__((noinline, section(".ram_text"))); + +public: + // public methods + SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false); + ~SoftwareSerial(); + void begin(long speed); + bool listen(); + void end(); + bool isListening() { return this == active_object; } + bool stopListening(); + bool overflow() + { + bool ret = _buffer_overflow; + if (ret) + _buffer_overflow = false; + return ret; + } + int peek(); + + virtual size_t write(uint8_t byte) __attribute__((noinline, section(".ram_text"))); + virtual int read(); + virtual int available(); + virtual void flush(); + operator bool() { return true; } + + using Print::write; + + // public only for easy access by interrupt handlers + static void handle_interrupt() __attribute__((noinline, section(".ram_text"))); +}; + +#endif diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index f5824d0..c82dbe4 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -309,7 +309,7 @@ void TwoWire::onRequest( void (*function)(void) ) } // С function for trap handler -extern "C" void wire_handler_wrapper(void) +extern "C" void __attribute__((optimize("O3"))) wire_interrupt_handler(void) { twi_interruptHandler(); } diff --git a/libraries/Wire/src/utility/twi.c b/libraries/Wire/src/utility/twi.c index 2483806..1978655 100644 --- a/libraries/Wire/src/utility/twi.c +++ b/libraries/Wire/src/utility/twi.c @@ -88,36 +88,22 @@ void twi_deinit(void) { uint32_t EPICmask; - HAL_I2C_Disable(&hi2c); - // disable clock #if I2C_NUM == 0 hi2c.Instance = I2C_0; EPICmask = HAL_EPIC_I2C_0_MASK; - __HAL_PCC_I2C_0_CLK_DISABLE(); #elif I2C_NUM == 1 hi2c.Instance = I2C_1; EPICmask = HAL_EPIC_I2C_1_MASK; - __HAL_PCC_I2C_1_CLK_DISABLE(); #else #error "Unsupported I2C_NUM value in pins_arduino.h" #endif + HAL_I2C_Deinit(&hi2c); + // for slave mode disable interrupts from i2c if (hi2c.Init.Mode == HAL_I2C_MODE_SLAVE) HAL_EPIC_MaskLevelClear(EPICmask); - // reconfigure pins to z state - GPIO_InitTypeDef GPIO_InitStruct; - memset(&GPIO_InitStruct, 0, sizeof(GPIO_InitStruct)); - // SDA - GPIO_InitStruct.Pin = (HAL_PinsTypeDef)digitalPinToBitMask(PIN_WIRE_SDA); - GPIO_InitStruct.Mode = HAL_GPIO_MODE_GPIO_INPUT; - GPIO_InitStruct.Pull = HAL_GPIO_PULL_NONE; - HAL_GPIO_Init(digitalPinToPort(PIN_WIRE_SDA), &GPIO_InitStruct); - // SCL - GPIO_InitStruct.Pin = (HAL_PinsTypeDef)digitalPinToBitMask(PIN_WIRE_SCL); - HAL_GPIO_Init(digitalPinToPort(PIN_WIRE_SCL), &GPIO_InitStruct); - twiIsOn = false; } @@ -252,7 +238,7 @@ uint8_t twi_masterWriteTo(uint8_t address, uint8_t* data, uint8_t length, uint8_ // parse errors // check separately, because in hal libraries not all functions look at this - if (hi2c.Instance->ISR & I2C_ISR_NACKF_M) + if (HAL_I2C_Get_Interrupts_Status(&hi2c) & I2C_ISR_NACKF_M) hi2c.ErrorCode = I2C_ERROR_NACK; if (hi2c.ErrorCode == (HAL_I2C_ErrorTypeDef)I2C_ERROR_TIMEOUT) ret = I2C_TIMEOUT; // timeout else if (hi2c.ErrorCode == (HAL_I2C_ErrorTypeDef)I2C_ERROR_NACK) ret = I2C_NACK_DATA; // didn't receive ACK @@ -275,39 +261,35 @@ i2c_status_e twi_slaveWrite(uint8_t *txData, uint8_t bytesNum) if ((hi2c.ErrorCode != I2C_ERROR_NONE)) HAL_I2C_Reset(&hi2c); - // отправка данных + // send data HAL_StatusTypeDef error_code = HAL_OK; - - hi2c.Instance->CR2 &= ~I2C_CR2_RELOAD_M; + HAL_I2C_Clear_Reload(&hi2c); + if (!(HAL_I2C_Get_CR1_Content(&hi2c) & I2C_CR1_NOSTRETCH_M)) // NOSTRETCH = 0 + HAL_I2C_Reset_TXDR_Content(&hi2c); + HAL_I2C_Write_TXDR(&hi2c, txData[0]); // first recording is made in advance - if (!(hi2c.Instance->CR1 & I2C_CR1_NOSTRETCH_M)) // NOSTRETCH = 0 - hi2c.Instance->ISR |= I2C_ISR_TXE_M; // Reset TXDR contents - - hi2c.Instance->TXDR = txData[0]; // The first recording is made in advance - - // Запись байта + // write byte for (uint32_t tx_count = 1; tx_count < bytesNum; tx_count++) { if ((error_code = HAL_I2C_Slave_WaitTXIS(&hi2c, TIMEOUT_TICKS)) != HAL_OK) { - // неудачная запись - hi2c.Instance->ISR |= I2C_ISR_TXE_M; // Reset TXDR contents - hi2c.Instance->ICR |= I2C_ICR_STOPCF_M; // Clear the STOP detection flag on the bus + // failed to write + HAL_I2C_Reset_TXDR_Content(&hi2c); + HAL_I2C_Reset_Interrupt_Flag(&hi2c, I2C_ICR_STOPCF_M); // Clear the STOP detection flag on the bus HAL_I2C_Reset(&hi2c); return I2C_TIMEOUT; } - hi2c.Instance->TXDR = txData[tx_count]; + HAL_I2C_Write_TXDR(&hi2c, txData[tx_count]); } if ((error_code = HAL_I2C_WaitBusy(&hi2c, TIMEOUT_TICKS)) != HAL_OK) { - // неудачное завершение транзакции + // failed to complete transaction HAL_I2C_Reset(&hi2c); return I2C_TIMEOUT; } - - hi2c.Instance->ISR |= I2C_ISR_TXE_M; // Reset TXDR contents - hi2c.Instance->ICR |= I2C_ICR_STOPCF_M; // Clear the STOP detection flag on the bus + HAL_I2C_Reset_TXDR_Content(&hi2c); + HAL_I2C_Reset_Interrupt_Flag(&hi2c, I2C_ICR_STOPCF_M); // Clear the STOP detection flag on the bus return I2C_OK; } @@ -341,27 +323,26 @@ void twi_attachSlaveTxEvent( void (*function)(void) ) * Input none * Output none */ -void twi_interruptHandler(void) +void __attribute__((optimize("O3"))) twi_interruptHandler(void) { - uint32_t int_mask = hi2c.Instance->CR1 & I2C_INTMASK; // interrupts allowed - uint32_t interrupt_status = hi2c.Instance->ISR; // current flags + uint32_t int_mask = HAL_I2C_Get_CR1_Content(&hi2c) & I2C_INTMASK; // interrupts allowed + uint32_t interrupt_status = HAL_I2C_Get_Interrupts_Status(&hi2c); // current flags // master calls by address, device in slave mode if ((interrupt_status & I2C_ISR_ADDR_M) && (int_mask & I2C_CR1_ADDRIE_M)) { // reset ADDR flag - hi2c.Instance->ICR |= I2C_ICR_ADDRCF_M; + HAL_I2C_Reset_Interrupt_Flag(&hi2c, I2C_ICR_ADDRCF_M); // look at the transmission direction and respond to the request if (interrupt_status & I2C_ISR_DIR_M) // master reads, slave sends - // отправляем данные - twi_onSlaveTransmit(); + twi_onSlaveTransmit(); // slave send data else // master writes, slave reads { twi_rxBufferIndex = 0; // write from the beginning of the buffer hi2c.State = HAL_I2C_STATE_BUSY; - hi2c.Instance->CR2 &= ~I2C_CR2_RELOAD_M; - // wait for interrupts because of receiving a byte or a stop condition + HAL_I2C_Clear_Reload(&hi2c); + // wait for interrupts by receiving a byte or a stop condition } } @@ -369,15 +350,15 @@ void twi_interruptHandler(void) if ((interrupt_status & I2C_ISR_RXNE_M) && (int_mask & I2C_CR1_RXIE_M)) { // put new byte into buffer - twi_rxBuffer[twi_rxBufferIndex++] = (uint8_t)hi2c.Instance->RXDR; + twi_rxBuffer[twi_rxBufferIndex++] = HAL_I2C_Get_RXDR(&hi2c); } // master sent a STOP to the bus if ((interrupt_status & I2C_ISR_STOPF_M) && (int_mask & I2C_CR1_STOPIE_M)) { hi2c.State = HAL_I2C_STATE_END; - hi2c.Instance->ISR |= I2C_ISR_TXE_M; // Reset TXDR contents - hi2c.Instance->ICR |= I2C_ICR_STOPCF_M; // Clear the STOP detection flag on the bus + HAL_I2C_Reset_TXDR_Content(&hi2c); + HAL_I2C_Reset_Interrupt_Flag(&hi2c, I2C_ICR_STOPCF_M); // Clear the STOP detection flag on the bus // pass the received data to callback function twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); } diff --git a/platform.txt b/platform.txt index 547e26d..a0a41a8 100644 --- a/platform.txt +++ b/platform.txt @@ -15,7 +15,7 @@ compiler.elf2hex.cmd={compiler.prefix}objcopy # Include directories and defines compiler.MIK32_Amur.extra_include= "-I{build.core.path}/avr/" "-I{build.core.path}/mik32/shared/include/" "-I{build.core.path}/mik32/shared/periphery/" "-I{build.core.path}/mik32/shared/runtime/" "-I{build.core.path}/mik32/shared/libs/" "-I{build.core.path}/mik32/hal/core/Include/" "-I{build.core.path}/mik32/hal/peripherals/Include/" "-I{build.core.path}/mik32/hal/utilities/Include/" -compiler.define="-DMCU_{build.mcu}" "-DF_CPU={build.f_cpu}" "-DARDUINO={runtime.ide.version}" "-DARDUINO_{build.board}" "-DARDUINO_ARCH_{build.arch}" "-DMIK32V2" "-D__ELBEAR_ACE_UNO__" "-D__RISC_V__" "-D__ARDUINO_ARC__" "-D__CPU_ARC__" +compiler.define="-DMCU_{build.mcu}" "-DF_CPU={build.f_cpu}" "-DARDUINO={runtime.ide.version}" "-DARDUINO_{build.board}" "-DARDUINO_ARCH_{build.arch}" "-DMIK32V2" "-D__ARDUINO_ARC__" "-D__CPU_ARC__" build.flags.optimize=-Os build.ldscript=spifi_cpp.ld @@ -31,7 +31,7 @@ compiler.extra_flags = -march=rv32imc_zicsr_zifencei -mabi=ilp32 -mcmodel=medlow compiler.S.flags = {compiler.extra_flags} -x assembler-with-cpp {compiler.define} {compiler.warning_flags} {compiler.MIK32_Amur.extra_include} compiler.c.flags = -c -std=gnu11 {compiler.extra_flags} {compiler.define} {compiler.warning_flags} {compiler.MIK32_Amur.extra_include} compiler.cpp.flags = -c -std=gnu++17 -fabi-version=0 -fno-exceptions -fno-rtti -fno-use-cxa-atexit -fno-threadsafe-statics {compiler.extra_flags} {compiler.define} {compiler.warning_flags} {compiler.MIK32_Amur.extra_include} -compiler.c.elf.flags = -march=rv32imc_zicsr_zifencei -mabi=ilp32 -mcmodel=medlow -nostartfiles -Xlinker +compiler.c.elf.flags = -march=rv32imc_zicsr_zifencei -mabi=ilp32 -mcmodel=medlow -nostartfiles -Xlinker compiler.ar.flags=rc compiler.elf2bin.flags=-O binary compiler.elf2hex.flags=-O ihex @@ -46,7 +46,7 @@ archive_file_path={build.path}/{archive_file} recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} "{archive_file_path}" "{object_file}" # Combine gc-sections, archives, and objects -recipe.c.combine.pattern= "{compiler.path}{compiler.c.elf.cmd}" -o "{build.path}/{build.project_name}.elf" -T "{build.core.path}/mik32/shared/ldscripts/{build.ldscript}" {compiler.c.elf.flags} -Map={build.path}/{build.project_name}.map -Wl,--gc-sections "-L{build.path}" -Wl,--start-group {object_files} -Wl,--whole-archive "{archive_file_path}" -Wl,--no-whole-archive -lc -Wl,--end-group +recipe.c.combine.pattern= "{compiler.path}{compiler.c.elf.cmd}" -o "{build.path}/{build.project_name}.elf" -T "{build.core.path}/mik32/shared/ldscripts/{build.ldscript}" {compiler.c.elf.flags} -Map={build.path}/{build.project_name}.map -Wl,--gc-sections -Wl,--no-warn-rwx-segments "-L{build.path}" -Wl,--start-group {object_files} -Wl,--whole-archive "{archive_file_path}" -Wl,--no-whole-archive -lc -Wl,--end-group # Create output (.bin file) recipe.objcopy.bin.pattern="{compiler.path}{compiler.objcopy.cmd}" {compiler.elf2bin.flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" diff --git a/variants/standart/pins_arduino.h b/variants/standart/pins_arduino.h index dabd353..404f7da 100644 --- a/variants/standart/pins_arduino.h +++ b/variants/standart/pins_arduino.h @@ -32,8 +32,6 @@ extern "C" { #include "mik32_hal_gpio.h" #include "mik32_hal_timer32.h" -extern bool spiNssPinIsBlocked; - // analog pins #define PIN_A0 (14) #define PIN_A1 (15) @@ -73,11 +71,13 @@ volatile uint32_t* portInputRegister(GPIO_TypeDef* GPIO_x); #define SERIAL_PORT_QTY 2 // ADC +#define MCU_ADC_RESOLUTION 12 // bits // determines the ADC channel number by the board pin number uint32_t analogInputToChannelNumber(uint32_t PinNumber); // PWM bool digitalPinHasPWM(uint8_t p); +bool digitalPinPwmIsOn(uint8_t digitalPin); // use only if digitalPinHasPWM() == true // determines which timer the pin belongs to TIMER32_TypeDef* pwmPinToTimer(uint32_t digPinNumber); // determines which timer channel the pin belongs to @@ -94,19 +94,13 @@ static const uint8_t MISO = PIN_SPI_MISO; static const uint8_t SCK = PIN_SPI_SCK; // config SEL_NSS1 to replace D10 to different controller pin, // because pin 1.3 which is D10 by default is needed to spi -inline void blockSpiPin(void) -{ - spiNssPinIsBlocked = true; -} -inline void unblockSpiPin(void) -{ - spiNssPinIsBlocked = false; -} +void spi_onBegin(void); +void spi_onEnd(void); // I2C #define PIN_WIRE_SDA (18) #define PIN_WIRE_SCL (19) -#define I2C_NUM (1) // i2c number 0 or 1 +#define I2C_NUM (1) // i2c number 1 static const uint8_t SDA = PIN_WIRE_SDA; static const uint8_t SCL = PIN_WIRE_SCL; // available frequencies @@ -115,16 +109,24 @@ static const uint8_t SCL = PIN_WIRE_SCL; #define WIRE_FREQ_1000K 1000000 // interrupts +extern uint8_t interruptInfo[EXTERNAL_NUM_INTERRUPTS][3]; // determines the board pin number by interrupt number -uint32_t interruptToDigitalPin(uint8_t interruptNum); +#define interruptToDigitalPin(interruptNum) (interruptInfo[interruptNum][0]) +// determines gpio interrupt line by interrupt number +#define interruptToGpioIntLine(interruptNum) ((uint8_t)interruptInfo[interruptNum][1]) +// determines gpio interrupt mux by interrupt number +#define interruptToGpioIntMux(interruptNum) ((uint8_t)interruptInfo[interruptNum][2]) // determines interrupt number by the board pin number int8_t digitalPinToInterrupt(uint32_t digPinNumber); -// determines gpio interrupt line by interrupt number -uint32_t interruptToGpioIntLine(uint8_t interruptNum); // determines interrupt number by the gpio interrupt line int8_t gpioIntLineToInterrupt(uint32_t gpioIntLine); -// determines gpio interrupt mux by interrupt number -uint32_t interruptToGpioIntMux(uint8_t interruptNum); +// determines gpio interrupt mux by the board pin number +int8_t digitalPinToGpioIntMux(uint8_t digPinNumber); +// determines gpio interrupt line by the board pin number +int8_t digitalPinToGpioIntLine(uint8_t digPinNumber); + + + #ifdef __cplusplus } diff --git a/variants/standart/variant.c b/variants/standart/variant.c index f38cf1f..a54ee4d 100644 --- a/variants/standart/variant.c +++ b/variants/standart/variant.c @@ -13,14 +13,8 @@ #include "pins_arduino.h" #include "mik32_hal_adc.h" - -#ifdef __cplusplus -extern "C" { -#endif -void ErrorMsgHandler(const char * msg); -#ifdef __cplusplus -} -#endif +#include "wiring_analog.h" +#include "wiring_LL.h" bool spiNssPinIsBlocked = false; @@ -141,6 +135,38 @@ uint32_t analogInputToChannelNumber(uint32_t PinNumber) } // ---------------------- PWM ---------------------- // +// use only if digitalPinHasPWM() == true +#define PWM_PIN_TO_PORT_NUMBER(pin) (((pin==10)||(pin==11)) ? 1:0) +// use only if digitalPinHasPWM() == true +static inline uint8_t pwmPinToGpioPinShift(uint8_t digitalPin) +{ + if (digitalPin == 3) + return 0; + else if ((digitalPin == 5) || (digitalPin == 11)) + return 1; + else if (digitalPin == 6) + return 2; + else // pins 9 10 + return 3; +} +// use only if digitalPinHasPWM() == true +// return true if digitalPin configured as pwm +bool digitalPinPwmIsOn(uint8_t digitalPin) +{ + uint8_t config = 0; + uint8_t pinShift = pwmPinToGpioPinShift(digitalPin); + + if (PWM_PIN_TO_PORT_NUMBER(digitalPin) == 0) + config = PIN_GET_PAD_CONFIG(PORT_0_CFG, pinShift); + else + config = PIN_GET_PAD_CONFIG(PORT_1_CFG, pinShift); + + if (config == 2) + return true; + else + return false; +} + bool digitalPinHasPWM(uint8_t p) { bool ret = false; @@ -179,9 +205,9 @@ HAL_TIMER32_CHANNEL_IndexTypeDef pwmPinToTimerChannel(uint32_t digPinNumber) } // ---------------------- interrupts ---------------------- // -// interrupt table +// interrupt table is stored in ram to improve performance // index = interrupt number. In each row {digitalPinNumber, IntLineValue, IntMuxValue} -const uint8_t interruptInfo[EXTERNAL_NUM_INTERRUPTS][3] = +uint8_t interruptInfo[EXTERNAL_NUM_INTERRUPTS][3] = { { 2, GPIO_LINE_2, GPIO_MUX_LINE_2_PORT0_10}, // INT0 { 3, GPIO_LINE_0, GPIO_MUX_LINE_0_PORT0_0}, // INT1 @@ -192,19 +218,23 @@ const uint8_t interruptInfo[EXTERNAL_NUM_INTERRUPTS][3] = {BTN_BUILTIN, GPIO_LINE_6, GPIO_MUX_LINE_6_PORT2_6}, // INT6 (button) }; -uint32_t interruptToDigitalPin(uint8_t interruptNum) +int8_t digitalPinToGpioIntMux(uint8_t digPinNumber) { - return interruptInfo[interruptNum][0]; + for (uint8_t i = 0; i < EXTERNAL_NUM_INTERRUPTS; i++) + { + if (interruptInfo[i][0] == digPinNumber) + return interruptInfo[i][2]; + } + return NOT_AN_INTERRUPT; } - -uint32_t interruptToGpioIntLine(uint8_t interruptNum) +int8_t digitalPinToGpioIntLine(uint8_t digPinNumber) { - return (uint32_t)interruptInfo[interruptNum][1]; -} - -uint32_t interruptToGpioIntMux(uint8_t interruptNum) -{ - return (uint32_t)interruptInfo[interruptNum][2]; + for (uint8_t i = 0; i < EXTERNAL_NUM_INTERRUPTS; i++) + { + if (interruptInfo[i][0] == digPinNumber) + return interruptInfo[i][1]; + } + return NOT_AN_INTERRUPT; } int8_t gpioIntLineToInterrupt(uint32_t gpioIntLine) @@ -225,4 +255,47 @@ int8_t digitalPinToInterrupt(uint32_t digPinNumber) return i; } return NOT_AN_INTERRUPT; +} + +// ---------------------- SPI ---------------------- // +void spi_onBegin(void) +{ + // On Elbear Ace-Uno rev1.1.0 there is a seller on pin 1.6 which replace D10 from spi NSS pin 1.3 to pin 1.4, + // because spi needs pin 1.3 for correct work + + // replace config from 1.3 to 1.4 + uint8_t config = PIN_GET_PAD_CONFIG(PORT_1_CFG, PIN_MASK_TO_PIN_NUMBER(GPIO_PIN_3)); + if (config == 0) // common gpio + { + // get info from pin gpio1.3 and set config to gpio1.4 + HAL_GPIO_PinConfig(GPIO_1, GPIO_PIN_4, HAL_GPIO_GetPinDirection(GPIO_1, GPIO_PIN_3), + (HAL_GPIO_PullTypeDef)PIN_GET_PAD_CONFIG(PORT_1_PUPD, PIN_MASK_TO_PIN_NUMBER(GPIO_PIN_3)), + HAL_GPIO_DS_2MA); + HAL_GPIO_WritePin(GPIO_1, GPIO_PIN_4, (GPIO_PinState)GPIO_GET_PIN_STATE(GPIO_1, PIN_MASK_TO_PIN_NUMBER(GPIO_PIN_3))); + + // pin D10 was switched to different gpio and can be used further + } + else if(config == 2) // timer for pwm + { + // if D10 (spi NSS pin) was used as pwm, we need to stop timer, because 1.4 don't support it + analogWriteStop(10); + ErrorMsgHandler("analogWrite(): D10 cannot be used as PWM pin while SPI is running"); + } + + // switch seller to pin 1.4 + HAL_GPIO_PinConfig(GPIO_1, GPIO_PIN_6, HAL_GPIO_MODE_GPIO_OUTPUT, HAL_GPIO_PULL_NONE, HAL_GPIO_DS_2MA); + HAL_GPIO_WritePin(GPIO_1, GPIO_PIN_6, GPIO_PIN_HIGH); + spiNssPinIsBlocked = true; // block spi pin +} + +void spi_onEnd(void) +{ + // get info from pin gpio1.4 and set config to gpio1.3 + HAL_GPIO_PinConfig(GPIO_1, GPIO_PIN_3, HAL_GPIO_GetPinDirection(GPIO_1, GPIO_PIN_4), + (HAL_GPIO_PullTypeDef)PIN_GET_PAD_CONFIG(PORT_1_PUPD, PIN_MASK_TO_PIN_NUMBER(GPIO_PIN_4)), HAL_GPIO_DS_2MA); + HAL_GPIO_WritePin(GPIO_1, GPIO_PIN_3, (GPIO_PinState)GPIO_GET_PIN_STATE(GPIO_1, PIN_MASK_TO_PIN_NUMBER(GPIO_PIN_4))); + + // switch seller back to pin 1.3 + HAL_GPIO_WritePin(GPIO_1, GPIO_PIN_6, GPIO_PIN_LOW); + spiNssPinIsBlocked = false; // unblock spi pin } \ No newline at end of file