cyfral_reader.h 7.9 KB

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