cyfral_reader.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #pragma once
  2. #include <furi.h>
  3. enum class CyfralReaderError : uint8_t {
  4. NO_ERROR = 0,
  5. UNABLE_TO_DETECT = 1,
  6. RAW_DATA_SIZE_ERROR = 2,
  7. UNKNOWN_NIBBLE_VALUE = 3,
  8. NO_START_NIBBLE = 4,
  9. };
  10. class CyfralReader {
  11. private:
  12. ADC_HandleTypeDef adc_config;
  13. ADC_TypeDef* adc_instance;
  14. uint32_t adc_channel;
  15. void get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level);
  16. void capture_data(bool* data, uint16_t capture_size, uint32_t line_min, uint32_t line_max);
  17. bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count);
  18. uint32_t search_array_in_array(
  19. const bool* haystack,
  20. const uint32_t haystack_size,
  21. const bool* needle,
  22. const uint32_t needle_size);
  23. // key is 9 nibbles
  24. static const uint16_t bits_in_nibble = 4;
  25. static const uint16_t key_length = 9;
  26. static const uint32_t capture_size = key_length * bits_in_nibble * 2;
  27. CyfralReaderError error;
  28. public:
  29. CyfralReader(ADC_TypeDef* adc, uint32_t Channel);
  30. ~CyfralReader();
  31. void start(void);
  32. void stop(void);
  33. bool read(uint8_t* data, uint8_t count);
  34. };
  35. void CyfralReader::get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level) {
  36. uint32_t in = 0;
  37. uint32_t min = UINT_MAX;
  38. uint32_t max = 0;
  39. for(uint32_t i = 0; i < 256; i++) {
  40. HAL_ADC_Start(&adc_config);
  41. HAL_ADC_PollForConversion(&adc_config, 100);
  42. in = HAL_ADC_GetValue(&adc_config);
  43. if(in < min) min = in;
  44. if(in > max) max = in;
  45. }
  46. *min_level = min;
  47. *max_level = max;
  48. }
  49. void CyfralReader::capture_data(
  50. bool* data,
  51. uint16_t capture_size,
  52. uint32_t line_min,
  53. uint32_t line_max) {
  54. uint32_t input_value = 0;
  55. bool last_input_value = 0;
  56. uint32_t diff = line_max - line_min;
  57. uint32_t mid = line_min + diff / 2;
  58. uint32_t low_threshold = mid - (diff / 4);
  59. uint32_t high_threshold = mid - (diff / 4);
  60. uint16_t capture_position = 0;
  61. uint32_t instructions_per_us = (SystemCoreClock / 1000000.0f);
  62. uint32_t time_threshold = 75 * instructions_per_us;
  63. uint32_t capture_max_time = 140 * (capture_size * 2) * instructions_per_us;
  64. uint32_t start = DWT->CYCCNT;
  65. uint32_t end = DWT->CYCCNT;
  66. memset(data, 0, capture_size);
  67. osKernelLock();
  68. uint32_t capture_start = DWT->CYCCNT;
  69. while((capture_position < capture_size) &&
  70. ((DWT->CYCCNT - capture_start) < capture_max_time)) {
  71. // read adc
  72. HAL_ADC_Start(&adc_config);
  73. HAL_ADC_PollForConversion(&adc_config, 100);
  74. input_value = HAL_ADC_GetValue(&adc_config);
  75. // low to high transition
  76. if((input_value > high_threshold) && last_input_value == 0) {
  77. last_input_value = 1;
  78. start = DWT->CYCCNT;
  79. }
  80. // high to low transition
  81. if((input_value < low_threshold) && last_input_value == 1) {
  82. last_input_value = 0;
  83. end = DWT->CYCCNT;
  84. // check transition time
  85. if(end - start < time_threshold) {
  86. data[capture_position] = 1;
  87. capture_position++;
  88. } else {
  89. data[capture_position] = 0;
  90. capture_position++;
  91. }
  92. }
  93. }
  94. osKernelUnlock();
  95. }
  96. uint32_t CyfralReader::search_array_in_array(
  97. const bool* haystack,
  98. const uint32_t haystack_size,
  99. const bool* needle,
  100. const uint32_t needle_size) {
  101. uint32_t haystack_index = 0, needle_index = 0;
  102. while(haystack_index < haystack_size && needle_index < needle_size) {
  103. if(haystack[haystack_index] == needle[needle_index]) {
  104. haystack_index++;
  105. needle_index++;
  106. if(needle_index == needle_size) {
  107. return (haystack_index - needle_size);
  108. };
  109. } else {
  110. haystack_index = haystack_index - needle_index + 1;
  111. needle_index = 0;
  112. }
  113. }
  114. return haystack_index;
  115. }
  116. bool CyfralReader::parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count) {
  117. const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0};
  118. uint32_t start_position =
  119. search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble);
  120. uint32_t end_position = 0;
  121. memset(data, 0, count);
  122. if(start_position < capture_size) {
  123. start_position = start_position + bits_in_nibble;
  124. end_position = start_position + count * 2 * bits_in_nibble;
  125. if(end_position >= capture_size) {
  126. error = CyfralReaderError::RAW_DATA_SIZE_ERROR;
  127. return false;
  128. }
  129. bool first_nibble = true;
  130. uint8_t data_position = 0;
  131. uint8_t nibble_value = 0;
  132. while(data_position < count) {
  133. nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 |
  134. !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3];
  135. switch(nibble_value) {
  136. case(0x7):
  137. case(0xB):
  138. case(0xD):
  139. case(0xE):
  140. break;
  141. default:
  142. error = CyfralReaderError::UNKNOWN_NIBBLE_VALUE;
  143. return false;
  144. break;
  145. }
  146. if(first_nibble) {
  147. data[data_position] |= nibble_value << 4;
  148. } else {
  149. data[data_position] |= nibble_value;
  150. }
  151. first_nibble = !first_nibble;
  152. if(first_nibble) {
  153. data_position++;
  154. }
  155. start_position = start_position + bits_in_nibble;
  156. }
  157. error = CyfralReaderError::NO_ERROR;
  158. return true;
  159. }
  160. error = CyfralReaderError::NO_START_NIBBLE;
  161. return false;
  162. }
  163. CyfralReader::CyfralReader(ADC_TypeDef* adc, uint32_t channel) {
  164. adc_instance = adc;
  165. adc_channel = channel;
  166. }
  167. CyfralReader::~CyfralReader() {
  168. }
  169. void CyfralReader::start(void) {
  170. ADC_ChannelConfTypeDef sConfig = {0};
  171. // init ADC
  172. adc_config.Instance = adc_instance;
  173. adc_config.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  174. adc_config.Init.Resolution = ADC_RESOLUTION_12B;
  175. adc_config.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  176. adc_config.Init.ScanConvMode = ADC_SCAN_DISABLE;
  177. adc_config.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  178. adc_config.Init.LowPowerAutoWait = DISABLE;
  179. adc_config.Init.ContinuousConvMode = DISABLE;
  180. adc_config.Init.NbrOfConversion = 1;
  181. adc_config.Init.DiscontinuousConvMode = DISABLE;
  182. adc_config.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  183. adc_config.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  184. adc_config.Init.DMAContinuousRequests = DISABLE;
  185. adc_config.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  186. adc_config.Init.OversamplingMode = DISABLE;
  187. if(HAL_ADC_Init(&adc_config) != HAL_OK) {
  188. Error_Handler();
  189. }
  190. // init channel
  191. sConfig.Channel = adc_channel;
  192. sConfig.Rank = ADC_REGULAR_RANK_1;
  193. sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  194. sConfig.SingleDiff = ADC_SINGLE_ENDED;
  195. sConfig.OffsetNumber = ADC_OFFSET_NONE;
  196. sConfig.Offset = 0;
  197. if(HAL_ADC_ConfigChannel(&adc_config, &sConfig) != HAL_OK) {
  198. Error_Handler();
  199. }
  200. }
  201. void CyfralReader::stop(void) {
  202. HAL_ADC_DeInit(&adc_config);
  203. }
  204. bool CyfralReader::read(uint8_t* data, uint8_t count) {
  205. uint32_t line_level_min, line_level_max;
  206. bool raw_data[capture_size];
  207. bool result = false;
  208. error = CyfralReaderError::NO_ERROR;
  209. // calibrate
  210. get_line_minmax(256, &line_level_min, &line_level_max);
  211. // TODO think about other detection method
  212. // key not on line
  213. if(line_level_max > 2000) {
  214. error = CyfralReaderError::UNABLE_TO_DETECT;
  215. return false;
  216. }
  217. // capturing raw data consisting of bits
  218. capture_data(raw_data, capture_size, line_level_min, line_level_max);
  219. // parse captured data
  220. if(parse_data(raw_data, capture_size, data, count)) {
  221. result = true;
  222. }
  223. return result;
  224. }