cyfral_reader_comp.h 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. #pragma once
  2. #include <furi.h>
  3. #include "callback-connector.h"
  4. #include <atomic>
  5. enum class CyfralReaderCompError : uint8_t {
  6. NO_ERROR = 0,
  7. UNABLE_TO_DETECT = 1,
  8. RAW_DATA_SIZE_ERROR = 2,
  9. UNKNOWN_NIBBLE_VALUE = 3,
  10. NO_START_NIBBLE = 4,
  11. NOT_ENOUGH_DATA = 5,
  12. };
  13. extern COMP_HandleTypeDef hcomp1;
  14. typedef struct {
  15. bool value;
  16. uint32_t dwt_value;
  17. } CompEvent;
  18. class CyfralReaderComp {
  19. private:
  20. bool capture_data(bool* data, uint16_t capture_size);
  21. bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count);
  22. uint32_t search_array_in_array(
  23. const bool* haystack,
  24. const uint32_t haystack_size,
  25. const bool* needle,
  26. const uint32_t needle_size);
  27. // key is 9 nibbles
  28. static const uint16_t bits_in_nibble = 4;
  29. static const uint16_t key_length = 9;
  30. static const uint32_t capture_size = key_length * bits_in_nibble * 2;
  31. CyfralReaderCompError error;
  32. const GpioPin* pin_record;
  33. std::atomic<bool> ready_to_process;
  34. void comparator_trigger_callback(void* hcomp, void* comp_ctx);
  35. osMessageQueueId_t comp_event_queue;
  36. public:
  37. CyfralReaderComp(const GpioPin* emulate_pin);
  38. ~CyfralReaderComp();
  39. void start(void);
  40. void stop(void);
  41. bool read(uint8_t* data, uint8_t count);
  42. };
  43. bool CyfralReaderComp::capture_data(bool* data, uint16_t capture_size) {
  44. uint32_t prev_timing = 0;
  45. uint16_t data_index = 0;
  46. CompEvent event_0, event_1;
  47. osStatus_t status;
  48. // read first event to get initial timing
  49. status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0);
  50. if(status != osOK) {
  51. return false;
  52. }
  53. prev_timing = event_0.dwt_value;
  54. // read second event until we get 0
  55. while(1) {
  56. status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0);
  57. if(status != osOK) {
  58. return false;
  59. }
  60. prev_timing = event_0.dwt_value;
  61. if(event_0.value == 0) break;
  62. }
  63. while(1) {
  64. // if event "zero" correct
  65. if(status == osOK && event_0.value == 0) {
  66. // get timing
  67. event_0.dwt_value -= prev_timing;
  68. prev_timing += event_0.dwt_value;
  69. // read next event
  70. status = osMessageQueueGet(comp_event_queue, &event_1, NULL, 0);
  71. // if event "one" correct
  72. if(status == osOK && event_1.value == 1) {
  73. // get timing
  74. event_1.dwt_value -= prev_timing;
  75. prev_timing += event_1.dwt_value;
  76. // calculate percentage of event "one" to full timing
  77. uint32_t full_timing = event_0.dwt_value + event_1.dwt_value;
  78. uint32_t percentage_1 = 1000000 / full_timing * event_1.dwt_value;
  79. // write captured data
  80. data[data_index] = percentage_1 > 500000 ? 0 : 1;
  81. data_index++;
  82. if(data_index >= capture_size) return true;
  83. status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0);
  84. } else {
  85. return false;
  86. }
  87. } else {
  88. return false;
  89. }
  90. }
  91. osMessageQueueReset(comp_event_queue);
  92. }
  93. uint32_t CyfralReaderComp::search_array_in_array(
  94. const bool* haystack,
  95. const uint32_t haystack_size,
  96. const bool* needle,
  97. const uint32_t needle_size) {
  98. uint32_t haystack_index = 0, needle_index = 0;
  99. while(haystack_index < haystack_size && needle_index < needle_size) {
  100. if(haystack[haystack_index] == needle[needle_index]) {
  101. haystack_index++;
  102. needle_index++;
  103. if(needle_index == needle_size) {
  104. return (haystack_index - needle_size);
  105. };
  106. } else {
  107. haystack_index = haystack_index - needle_index + 1;
  108. needle_index = 0;
  109. }
  110. }
  111. return haystack_index;
  112. }
  113. void CyfralReaderComp::comparator_trigger_callback(void* hcomp, void* comp_ctx) {
  114. CyfralReaderComp* _this = static_cast<CyfralReaderComp*>(comp_ctx);
  115. COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp);
  116. // check that hw is comparator 1
  117. if(_hcomp != &hcomp1) return;
  118. // if queue if not full
  119. if(_this->ready_to_process == false) {
  120. // send event to queue
  121. CompEvent event;
  122. // TOOD F4 and F5 differ
  123. event.value = (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_LOW);
  124. event.dwt_value = DWT->CYCCNT;
  125. osStatus_t status = osMessageQueuePut(_this->comp_event_queue, &event, 0, 0);
  126. // queue is full, so we need to process data
  127. if(status != osOK) {
  128. _this->ready_to_process = true;
  129. };
  130. }
  131. }
  132. bool CyfralReaderComp::parse_data(
  133. bool* raw_data,
  134. uint16_t capture_size,
  135. uint8_t* data,
  136. uint8_t count) {
  137. const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0};
  138. uint32_t start_position =
  139. search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble);
  140. uint32_t end_position = 0;
  141. memset(data, 0, count);
  142. if(start_position < capture_size) {
  143. start_position = start_position + bits_in_nibble;
  144. end_position = start_position + count * 2 * bits_in_nibble;
  145. if(end_position >= capture_size) {
  146. error = CyfralReaderCompError::RAW_DATA_SIZE_ERROR;
  147. return false;
  148. }
  149. bool first_nibble = true;
  150. uint8_t data_position = 0;
  151. uint8_t nibble_value = 0;
  152. while(data_position < count) {
  153. nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 |
  154. !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3];
  155. switch(nibble_value) {
  156. case(0x7):
  157. case(0xB):
  158. case(0xD):
  159. case(0xE):
  160. break;
  161. default:
  162. error = CyfralReaderCompError::UNKNOWN_NIBBLE_VALUE;
  163. return false;
  164. break;
  165. }
  166. if(first_nibble) {
  167. data[data_position] |= nibble_value << 4;
  168. } else {
  169. data[data_position] |= nibble_value;
  170. }
  171. first_nibble = !first_nibble;
  172. if(first_nibble) {
  173. data_position++;
  174. }
  175. start_position = start_position + bits_in_nibble;
  176. }
  177. error = CyfralReaderCompError::NO_ERROR;
  178. return true;
  179. }
  180. error = CyfralReaderCompError::NO_START_NIBBLE;
  181. return false;
  182. }
  183. CyfralReaderComp::CyfralReaderComp(const GpioPin* gpio_pin) {
  184. pin_record = gpio_pin;
  185. }
  186. CyfralReaderComp::~CyfralReaderComp() {
  187. }
  188. void CyfralReaderComp::start(void) {
  189. // pulldown lf-rfid pins to prevent interference
  190. // TODO open record
  191. GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
  192. gpio_init((GpioPin*)&rfid_pull_pin, GpioModeOutputOpenDrain);
  193. gpio_write((GpioPin*)&rfid_pull_pin, false);
  194. // TODO open record
  195. GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
  196. gpio_init((GpioPin*)&rfid_out_pin, GpioModeOutputOpenDrain);
  197. gpio_write((GpioPin*)&rfid_out_pin, false);
  198. // connect comparator callback
  199. void* comp_ctx = this;
  200. comp_event_queue = osMessageQueueNew(capture_size * 2 + 2, sizeof(CompEvent), NULL);
  201. ready_to_process = false;
  202. auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback);
  203. api_interrupt_add(cmp_cb, InterruptTypeComparatorTrigger, comp_ctx);
  204. // start comaparator
  205. HAL_COMP_Start(&hcomp1);
  206. }
  207. void CyfralReaderComp::stop(void) {
  208. // stop comaparator
  209. HAL_COMP_Stop(&hcomp1);
  210. // disconnect comparator callback
  211. auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback);
  212. api_interrupt_remove(cmp_cb, InterruptTypeComparatorTrigger);
  213. osMessageQueueDelete(comp_event_queue);
  214. }
  215. bool CyfralReaderComp::read(uint8_t* data, uint8_t count) {
  216. bool raw_data[capture_size];
  217. bool result = false;
  218. error = CyfralReaderCompError::NO_ERROR;
  219. if(ready_to_process == false) {
  220. error = CyfralReaderCompError::NOT_ENOUGH_DATA;
  221. } else {
  222. memset(raw_data, 0, sizeof(bool) * capture_size);
  223. if(capture_data(raw_data, capture_size)) {
  224. if(parse_data(raw_data, capture_size, data, count)) {
  225. result = true;
  226. }
  227. }
  228. ready_to_process = false;
  229. }
  230. return result;
  231. }