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;
|
|
} |