 8f9b2513ff
			
		
	
	
		8f9b2513ff
		
			
		
	
	
	
	
		
			
			* SYSTEM: tickless mode with deep sleep. * Move FreeRTOS ticks to lptim2 * API: move all sumbodules init routines to one place. Timebase: working lptim2 at tick source. * API Timebase: lp-timer routines, timer access safe zones prediction and synchronization. FreeRTOS: adjust configuration for tickless mode. * NFC: support for tickless mode. * API Timebase: improve tick error handling in IRQ. Apploader: use insomnia mode to run applications. * BLE: prevent sleep while core2 starting * HAL: nap while in insomnia mode * init records work * try to implement record delete * tests and flapp * flapp subsystem * new core functions to get app stat, simplify core code * fix thread termination * add strdup to core * fix tests * Refactoring: remove all unusued parts, update API usage, aggreagate API sources and headers, new record storage * Refactoring: update furi record api usage, cleanup code * Fix broken merge for freertos apps * Core, Target: fix compilation warnings * Drop firmware target local * HAL Timebase, Power, Clock: semaphore guarded access to clock and power modes, better sleep mode. * SD-Filesystem: wait for all deps to arrive before adding widget. Core, BLE: disable debug dump to serial. * delete old app example-ipc * delete old app fatfs list * fix strobe app, add input header * delete old display driver * comment old app qr-code * fix sd-card test, add forced widget update * remove unused new core test * increase heap to 128k * comment and assert old core tests * fix syntax Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
		
			
				
	
	
		
			272 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			7.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| #include <furi.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;
 | |
| } |