| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- #pragma once
- #include "flipper.h"
- #include "flipper_v2.h"
- enum class CyfralReaderError : uint8_t {
- NO_ERROR = 0,
- UNABLE_TO_DETECT = 1,
- RAW_DATA_SIZE_ERROR = 2,
- UNKNOWN_NIBBLE_VALUE = 3,
- NO_START_NIBBLE = 4,
- };
- class CyfralReader {
- private:
- ADC_HandleTypeDef adc_config;
- ADC_TypeDef* adc_instance;
- uint32_t adc_channel;
- void get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level);
- void capture_data(bool* data, uint16_t capture_size, uint32_t line_min, uint32_t line_max);
- bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count);
- uint32_t search_array_in_array(
- const bool* haystack,
- const uint32_t haystack_size,
- const bool* needle,
- const uint32_t needle_size);
- // key is 9 nibbles
- static const uint16_t bits_in_nibble = 4;
- static const uint16_t key_length = 9;
- static const uint32_t capture_size = key_length * bits_in_nibble * 2;
- CyfralReaderError error;
- public:
- CyfralReader(ADC_TypeDef* adc, uint32_t Channel);
- ~CyfralReader();
- void start(void);
- void stop(void);
- bool read(uint8_t* data, uint8_t count);
- };
- void CyfralReader::get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level) {
- uint32_t in = 0;
- uint32_t min = UINT_MAX;
- uint32_t max = 0;
- for(uint32_t i = 0; i < 256; i++) {
- HAL_ADC_Start(&adc_config);
- HAL_ADC_PollForConversion(&adc_config, 100);
- in = HAL_ADC_GetValue(&adc_config);
- if(in < min) min = in;
- if(in > max) max = in;
- }
- *min_level = min;
- *max_level = max;
- }
- void CyfralReader::capture_data(
- bool* data,
- uint16_t capture_size,
- uint32_t line_min,
- uint32_t line_max) {
- uint32_t input_value = 0;
- bool last_input_value = 0;
- uint32_t diff = line_max - line_min;
- uint32_t mid = line_min + diff / 2;
- uint32_t low_threshold = mid - (diff / 4);
- uint32_t high_threshold = mid - (diff / 4);
- uint16_t capture_position = 0;
- uint32_t instructions_per_us = (SystemCoreClock / 1000000.0f);
- uint32_t time_threshold = 75 * instructions_per_us;
- uint32_t capture_max_time = 140 * (capture_size * 2) * instructions_per_us;
- uint32_t start = DWT->CYCCNT;
- uint32_t end = DWT->CYCCNT;
- memset(data, 0, capture_size);
- osKernelLock();
- uint32_t capture_start = DWT->CYCCNT;
- while((capture_position < capture_size) &&
- ((DWT->CYCCNT - capture_start) < capture_max_time)) {
- // read adc
- HAL_ADC_Start(&adc_config);
- HAL_ADC_PollForConversion(&adc_config, 100);
- input_value = HAL_ADC_GetValue(&adc_config);
- // low to high transition
- if((input_value > high_threshold) && last_input_value == 0) {
- last_input_value = 1;
- start = DWT->CYCCNT;
- }
- // high to low transition
- if((input_value < low_threshold) && last_input_value == 1) {
- last_input_value = 0;
- end = DWT->CYCCNT;
- // check transition time
- if(end - start < time_threshold) {
- data[capture_position] = 1;
- capture_position++;
- } else {
- data[capture_position] = 0;
- capture_position++;
- }
- }
- }
- osKernelUnlock();
- }
- uint32_t CyfralReader::search_array_in_array(
- const bool* haystack,
- const uint32_t haystack_size,
- const bool* needle,
- const uint32_t needle_size) {
- uint32_t haystack_index = 0, needle_index = 0;
- while(haystack_index < haystack_size && needle_index < needle_size) {
- if(haystack[haystack_index] == needle[needle_index]) {
- haystack_index++;
- needle_index++;
- if(needle_index == needle_size) {
- return (haystack_index - needle_size);
- };
- } else {
- haystack_index = haystack_index - needle_index + 1;
- needle_index = 0;
- }
- }
- return haystack_index;
- }
- bool CyfralReader::parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count) {
- const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0};
- uint32_t start_position =
- search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble);
- uint32_t end_position = 0;
- memset(data, 0, count);
- if(start_position < capture_size) {
- start_position = start_position + bits_in_nibble;
- end_position = start_position + count * 2 * bits_in_nibble;
- if(end_position >= capture_size) {
- error = CyfralReaderError::RAW_DATA_SIZE_ERROR;
- return false;
- }
- bool first_nibble = true;
- uint8_t data_position = 0;
- uint8_t nibble_value = 0;
- while(data_position < count) {
- nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 |
- !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3];
- switch(nibble_value) {
- case(0x7):
- case(0xB):
- case(0xD):
- case(0xE):
- break;
- default:
- error = CyfralReaderError::UNKNOWN_NIBBLE_VALUE;
- return false;
- break;
- }
- if(first_nibble) {
- data[data_position] |= nibble_value << 4;
- } else {
- data[data_position] |= nibble_value;
- }
- first_nibble = !first_nibble;
- if(first_nibble) {
- data_position++;
- }
- start_position = start_position + bits_in_nibble;
- }
- error = CyfralReaderError::NO_ERROR;
- return true;
- }
- error = CyfralReaderError::NO_START_NIBBLE;
- return false;
- }
- CyfralReader::CyfralReader(ADC_TypeDef* adc, uint32_t channel) {
- adc_instance = adc;
- adc_channel = channel;
- }
- CyfralReader::~CyfralReader() {
- }
- void CyfralReader::start(void) {
- ADC_ChannelConfTypeDef sConfig = {0};
- // init ADC
- adc_config.Instance = adc_instance;
- adc_config.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
- adc_config.Init.Resolution = ADC_RESOLUTION_12B;
- adc_config.Init.DataAlign = ADC_DATAALIGN_RIGHT;
- adc_config.Init.ScanConvMode = ADC_SCAN_DISABLE;
- adc_config.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
- adc_config.Init.LowPowerAutoWait = DISABLE;
- adc_config.Init.ContinuousConvMode = DISABLE;
- adc_config.Init.NbrOfConversion = 1;
- adc_config.Init.DiscontinuousConvMode = DISABLE;
- adc_config.Init.ExternalTrigConv = ADC_SOFTWARE_START;
- adc_config.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
- adc_config.Init.DMAContinuousRequests = DISABLE;
- adc_config.Init.Overrun = ADC_OVR_DATA_PRESERVED;
- adc_config.Init.OversamplingMode = DISABLE;
- if(HAL_ADC_Init(&adc_config) != HAL_OK) {
- Error_Handler();
- }
- // init channel
- sConfig.Channel = adc_channel;
- sConfig.Rank = ADC_REGULAR_RANK_1;
- sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
- sConfig.SingleDiff = ADC_SINGLE_ENDED;
- sConfig.OffsetNumber = ADC_OFFSET_NONE;
- sConfig.Offset = 0;
- if(HAL_ADC_ConfigChannel(&adc_config, &sConfig) != HAL_OK) {
- Error_Handler();
- }
- }
- void CyfralReader::stop(void) {
- HAL_ADC_DeInit(&adc_config);
- }
- bool CyfralReader::read(uint8_t* data, uint8_t count) {
- uint32_t line_level_min, line_level_max;
- bool raw_data[capture_size];
- bool result = false;
- error = CyfralReaderError::NO_ERROR;
- // calibrate
- get_line_minmax(256, &line_level_min, &line_level_max);
- // TODO think about other detection method
- // key not on line
- if(line_level_max > 2000) {
- error = CyfralReaderError::UNABLE_TO_DETECT;
- return false;
- }
- // capturing raw data consisting of bits
- capture_data(raw_data, capture_size, line_level_min, line_level_max);
- // parse captured data
- if(parse_data(raw_data, capture_size, data, count)) {
- result = true;
- }
- return result;
- }
|