From ccd40497ebae6f3da27af4e565cf5f048562e3aa Mon Sep 17 00:00:00 2001 From: coreglitch Date: Thu, 19 Nov 2020 14:11:03 +0300 Subject: [PATCH] FL-176 LF RFID RX (#248) * pulldown ibutton pin during rfid read * enable and handle COMP interrupts * send events from comparator IRQ and handle in app * manchester encode * successfully read em4100 * read-emulate * led --- applications/lf-rfid/em4100.c | 4 +- applications/lf-rfid/lf-rfid.c | 301 +++++++++++++++++++++---- firmware/targets/f3/Inc/stm32wbxx_it.h | 1 + firmware/targets/f3/Src/comp.c | 13 +- firmware/targets/f3/Src/spi.c | 1 - firmware/targets/f3/Src/stm32wbxx_it.c | 15 ++ firmware/targets/f3/f3.ioc | 11 +- 7 files changed, 288 insertions(+), 58 deletions(-) diff --git a/applications/lf-rfid/em4100.c b/applications/lf-rfid/em4100.c index b4a396b0..43b0893d 100644 --- a/applications/lf-rfid/em4100.c +++ b/applications/lf-rfid/em4100.c @@ -43,11 +43,13 @@ void prepare_data(uint32_t ID, uint32_t VENDOR, uint8_t* data) { data[63] = 0; // stop bit + /* printf("em data: "); for(uint8_t i = 0; i < 64; i++) { printf("%d ", data[i]); } printf("\n"); + */ } void em4100_emulation(uint8_t* data, GpioPin* pin) { @@ -65,4 +67,4 @@ void em4100_emulation(uint8_t* data, GpioPin* pin) { gpio_write(pin, false); taskEXIT_CRITICAL(); -} \ No newline at end of file +} diff --git a/applications/lf-rfid/lf-rfid.c b/applications/lf-rfid/lf-rfid.c index 9afde95d..e1fdc79d 100644 --- a/applications/lf-rfid/lf-rfid.c +++ b/applications/lf-rfid/lf-rfid.c @@ -1,13 +1,16 @@ #include "flipper_v2.h" -typedef enum { - EventTypeTick, - EventTypeKey, -} EventType; +typedef enum { EventTypeTick, EventTypeKey, EventTypeRx } EventType; + +typedef struct { + bool value; + uint32_t dwt_value; +} RxEvent; typedef struct { union { InputEvent input; + RxEvent rx; } value; EventType type; } AppEvent; @@ -15,6 +18,8 @@ typedef struct { typedef struct { uint32_t freq_khz; bool on; + uint8_t customer_id; + uint32_t em_data; } State; static void render_callback(CanvasApi* canvas, void* ctx) { @@ -26,11 +31,16 @@ static void render_callback(CanvasApi* canvas, void* ctx) { canvas->set_font(canvas, FontPrimary); canvas->draw_str(canvas, 2, 12, "LF RFID"); - canvas->draw_str(canvas, 2, 24, state->on ? "ON" : "OFF"); - char buf[12]; + canvas->draw_str(canvas, 2, 24, state->on ? "Reading" : "Emulating"); + + char buf[14]; + sprintf(buf, "%d kHz", (int)state->freq_khz); canvas->draw_str(canvas, 2, 36, buf); + sprintf(buf, "%02d:%010ld", state->customer_id, state->em_data); + canvas->draw_str(canvas, 2, 45, buf); + release_mutex((ValueMutex*)ctx, state); } @@ -47,6 +57,109 @@ extern TIM_HandleTypeDef TIM_C; void em4100_emulation(uint8_t* data, GpioPin* pin); void prepare_data(uint32_t ID, uint32_t VENDOR, uint8_t* data); +GpioPin debug_0 = {.pin = GPIO_PIN_2, .port = GPIOB}; +GpioPin debug_1 = {.pin = GPIO_PIN_3, .port = GPIOC}; + +extern COMP_HandleTypeDef hcomp1; + +void* comp_ctx = NULL; + +void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) { + if(hcomp != &hcomp1) return; + + // gpio_write(&debug_0, true); + + osMessageQueueId_t event_queue = (QueueHandle_t)comp_ctx; + + AppEvent event; + event.type = EventTypeRx; + event.value.rx.value = (HAL_COMP_GetOutputLevel(hcomp) == COMP_OUTPUT_LEVEL_HIGH); + event.value.rx.dwt_value = DWT->CYCCNT; + osMessageQueuePut(event_queue, &event, 0, 0); + + // gpio_write(&debug_0, false); +} + +const uint8_t ROW_SIZE = 4; +const uint8_t LINE_SIZE = 10; + +static bool even_check(uint8_t* buf) { + uint8_t col_parity_sum[ROW_SIZE]; + for(uint8_t col = 0; col < ROW_SIZE; col++) { + col_parity_sum[col] = 0; + } + + // line parity + for(uint8_t line = 0; line < LINE_SIZE; line++) { + printf("%d: ", line); + uint8_t parity_sum = 0; + for(uint8_t col = 0; col < ROW_SIZE; col++) { + parity_sum += buf[line * (ROW_SIZE + 1) + col]; + col_parity_sum[col] += buf[line * (ROW_SIZE + 1) + col]; + printf("%d ", buf[line * (ROW_SIZE + 1) + col]); + } + if((1 & parity_sum) != buf[line * (ROW_SIZE + 1) + ROW_SIZE]) { + printf( + "line parity fail at %d (%d : %d)\n", + line, + parity_sum, + buf[line * (ROW_SIZE + 1) + ROW_SIZE]); + return false; + } + printf("\n"); + } + + for(uint8_t col = 0; col < ROW_SIZE; col++) { + if((1 & col_parity_sum[col]) != buf[LINE_SIZE * (ROW_SIZE + 1) + col]) { + printf( + "col parity fail at %d (%d : %d)\n", + col, + col_parity_sum[col], + buf[LINE_SIZE * (ROW_SIZE + 1) + col]); + return false; + } + } + + return true; +} + +static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) { + uint32_t data = 0; + uint8_t offset = 0; + + printf("customer: "); + for(uint8_t line = 0; line < 2; line++) { + for(uint8_t col = 0; col < ROW_SIZE; col++) { + uint32_t bit = buf[line * (ROW_SIZE + 1) + col]; + + data |= bit << (7 - offset); + printf("%d ", bit); + + offset++; + } + } + printf("\n"); + + *customer = data; + + data = 0; + offset = 0; + printf("data: "); + for(uint8_t line = 2; line < LINE_SIZE; line++) { + for(uint8_t col = 0; col < ROW_SIZE; col++) { + uint32_t bit = buf[line * (ROW_SIZE + 1) + col]; + + data |= bit << (31 - offset); + printf("%d ", bit); + + offset++; + } + } + printf("\n"); + + *em_data = data; +} + void lf_rfid_workaround(void* p) { osMessageQueueId_t event_queue = osMessageQueueNew(1, sizeof(AppEvent), NULL); @@ -57,12 +170,25 @@ void lf_rfid_workaround(void* p) { gpio_init(pull_pin_record, GpioModeOutputPushPull); + gpio_init(&debug_0, GpioModeOutputPushPull); + gpio_init(&debug_1, GpioModeOutputPushPull); + + // pulldown iBtn pin to prevent interference from ibutton + gpio_init((GpioPin*)&ibutton_gpio, GpioModeOutputOpenDrain); + gpio_write((GpioPin*)&ibutton_gpio, false); + + // init ctx + comp_ctx = (void*)event_queue; + // start comp + HAL_COMP_Start(&hcomp1); + uint8_t emulation_data[64]; - prepare_data(4378151, 01, emulation_data); State _state; _state.freq_khz = 125; _state.on = false; + _state.customer_id = 01; + _state.em_data = 4378151; ValueMutex state_mutex; if(!init_mutex(&state_mutex, &_state, sizeof(State))) { @@ -84,55 +210,132 @@ void lf_rfid_workaround(void* p) { gui->add_widget(gui, widget, GuiLayerFullscreen); AppEvent event; + uint32_t prev_dwt; + int8_t symbol = -1; // init state + bool center = false; + size_t symbol_cnt = 0; + + GpioPin* led_record = (GpioPin*)&led_gpio[1]; + gpio_init(led_record, GpioModeOutputOpenDrain); + + uint8_t buf[64]; + for(size_t i = 0; i < 64; i++) { + buf[i] = 0; + } + while(1) { osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100); - State* state = (State*)acquire_mutex_block(&state_mutex); - if(event_status == osOK) { - if(event.type == EventTypeKey) { - // press events - if(event.value.input.state && event.value.input.input == InputBack) { - hal_pwmn_stop(&TIM_C, TIM_CHANNEL_1); // TODO: move to furiac_onexit - gpio_init(pull_pin_record, GpioModeInput); - // TODO remove all widgets create by app - widget_enabled_set(widget, false); - furiac_exit(NULL); + if(event.type == EventTypeRx && event_status == osOK) { + uint32_t dt = (event.value.rx.dwt_value - prev_dwt) / (SystemCoreClock / 1000000.0f); + prev_dwt = event.value.rx.dwt_value; + + if(dt > 384) { + // change symbol 0->1 or 1->0 + symbol = event.value.rx.value; + center = true; + } else { + // same symbol as prev or center + center = !center; + } + + /* + gpio_write(&debug_1, true); + delay_us(center ? 10 : 30); + gpio_write(&debug_1, false); + */ + + if(center && symbol != -1) { + /* + gpio_write(&debug_0, true); + delay_us(symbol ? 10 : 30); + gpio_write(&debug_0, false); + */ + + buf[symbol_cnt] = symbol; + symbol_cnt++; + } + + // check preamble + if(symbol_cnt <= 9 && symbol == 0) { + symbol_cnt = 0; + symbol = -1; + } + + // check stop bit + if(symbol_cnt == 64 && symbol == 1) { + symbol_cnt = 0; + symbol = -1; + } + + if(symbol_cnt == 64) { + if(even_check(&buf[9])) { + State* state = (State*)acquire_mutex_block(&state_mutex); + extract_data(&buf[9], &state->customer_id, &state->em_data); + printf("customer: %02d, data: %010lu\n", state->customer_id, state->em_data); + release_mutex(&state_mutex, state); + gpio_write(led_record, false); + osDelay(100); + gpio_write(led_record, true); } - if(event.value.input.state && event.value.input.input == InputUp) { - state->freq_khz += 10; - } - - if(event.value.input.state && event.value.input.input == InputDown) { - state->freq_khz -= 10; - } - - if(event.value.input.state && event.value.input.input == InputLeft) { - } - - if(event.value.input.state && event.value.input.input == InputRight) { - } - - if(event.value.input.state && event.value.input.input == InputOk) { - state->on = !state->on; - } + symbol_cnt = 0; } } else { - // event timeout + State* state = (State*)acquire_mutex_block(&state_mutex); + + if(event_status == osOK) { + if(event.type == EventTypeKey) { + // press events + if(event.value.input.state && event.value.input.input == InputBack) { + hal_pwmn_stop(&TIM_C, TIM_CHANNEL_1); // TODO: move to furiac_onexit + gpio_init(pull_pin_record, GpioModeInput); + gpio_init((GpioPin*)&ibutton_gpio, GpioModeInput); + + // TODO remove all widgets create by app + widget_enabled_set(widget, false); + furiac_exit(NULL); + } + + if(event.value.input.state && event.value.input.input == InputUp) { + state->freq_khz += 10; + } + + if(event.value.input.state && event.value.input.input == InputDown) { + state->freq_khz -= 10; + } + + if(event.value.input.state && event.value.input.input == InputLeft) { + } + + if(event.value.input.state && event.value.input.input == InputRight) { + } + + if(event.value.input.state && event.value.input.input == InputOk) { + state->on = !state->on; + + if(!state->on) { + prepare_data(state->em_data, state->customer_id, emulation_data); + } + } + } + } else { + // event timeout + } + + hal_pwmn_set( + state->on ? 0.5 : 0.0, (float)(state->freq_khz * 1000), &LFRFID_TIM, LFRFID_CH); + + if(!state->on) { + em4100_emulation(emulation_data, pull_pin_record); + } else { + gpio_write(pull_pin_record, false); + } + + // common code, for example, force update UI + widget_update(widget); + + release_mutex(&state_mutex, state); } - - hal_pwmn_set( - state->on ? 0.5 : 0.0, (float)(state->freq_khz * 1000), &LFRFID_TIM, LFRFID_CH); - - if(!state->on) { - em4100_emulation(emulation_data, pull_pin_record); - } else { - gpio_write(pull_pin_record, false); - } - - // common code, for example, force update UI - widget_update(widget); - - release_mutex(&state_mutex, state); } } diff --git a/firmware/targets/f3/Inc/stm32wbxx_it.h b/firmware/targets/f3/Inc/stm32wbxx_it.h index f646f87c..f2ef1d60 100644 --- a/firmware/targets/f3/Inc/stm32wbxx_it.h +++ b/firmware/targets/f3/Inc/stm32wbxx_it.h @@ -56,6 +56,7 @@ void DebugMon_Handler(void); void EXTI1_IRQHandler(void); void EXTI2_IRQHandler(void); void USB_LP_IRQHandler(void); +void COMP_IRQHandler(void); void EXTI9_5_IRQHandler(void); void TIM1_TRG_COM_TIM17_IRQHandler(void); void TIM2_IRQHandler(void); diff --git a/firmware/targets/f3/Src/comp.c b/firmware/targets/f3/Src/comp.c index f17c3e3f..f8d69ed4 100644 --- a/firmware/targets/f3/Src/comp.c +++ b/firmware/targets/f3/Src/comp.c @@ -31,14 +31,14 @@ void MX_COMP1_Init(void) { hcomp1.Instance = COMP1; - hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; + hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_4VREFINT; hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1; hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED; - hcomp1.Init.Hysteresis = COMP_HYSTERESIS_NONE; + hcomp1.Init.Hysteresis = COMP_HYSTERESIS_HIGH; hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE; - hcomp1.Init.Mode = COMP_POWERMODE_HIGHSPEED; + hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED; hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; - hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_NONE; + hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING; if (HAL_COMP_Init(&hcomp1) != HAL_OK) { Error_Handler(); @@ -65,6 +65,9 @@ void HAL_COMP_MspInit(COMP_HandleTypeDef* compHandle) GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(RFID_RF_IN_GPIO_Port, &GPIO_InitStruct); + /* COMP1 interrupt Init */ + HAL_NVIC_SetPriority(COMP_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(COMP_IRQn); /* USER CODE BEGIN COMP1_MspInit 1 */ /* USER CODE END COMP1_MspInit 1 */ @@ -85,6 +88,8 @@ void HAL_COMP_MspDeInit(COMP_HandleTypeDef* compHandle) */ HAL_GPIO_DeInit(RFID_RF_IN_GPIO_Port, RFID_RF_IN_Pin); + /* COMP1 interrupt Deinit */ + HAL_NVIC_DisableIRQ(COMP_IRQn); /* USER CODE BEGIN COMP1_MspDeInit 1 */ /* USER CODE END COMP1_MspDeInit 1 */ diff --git a/firmware/targets/f3/Src/spi.c b/firmware/targets/f3/Src/spi.c index 07d1aa3a..631b319d 100644 --- a/firmware/targets/f3/Src/spi.c +++ b/firmware/targets/f3/Src/spi.c @@ -19,7 +19,6 @@ /* Includes ------------------------------------------------------------------*/ #include "spi.h" -#include "cmsis_os.h" /* USER CODE BEGIN 0 */ void Enable_SPI(SPI_HandleTypeDef* spi); diff --git a/firmware/targets/f3/Src/stm32wbxx_it.c b/firmware/targets/f3/Src/stm32wbxx_it.c index 09e2a940..ae42583a 100644 --- a/firmware/targets/f3/Src/stm32wbxx_it.c +++ b/firmware/targets/f3/Src/stm32wbxx_it.c @@ -57,6 +57,7 @@ /* External variables --------------------------------------------------------*/ extern PCD_HandleTypeDef hpcd_USB_FS; +extern COMP_HandleTypeDef hcomp1; extern TIM_HandleTypeDef htim1; extern TIM_HandleTypeDef htim2; extern TIM_HandleTypeDef htim17; @@ -206,6 +207,20 @@ void USB_LP_IRQHandler(void) /* USER CODE END USB_LP_IRQn 1 */ } +/** + * @brief This function handles COMP1 and COMP2 interrupts through EXTI lines 20 and 21. + */ +void COMP_IRQHandler(void) +{ + /* USER CODE BEGIN COMP_IRQn 0 */ + + /* USER CODE END COMP_IRQn 0 */ + HAL_COMP_IRQHandler(&hcomp1); + /* USER CODE BEGIN COMP_IRQn 1 */ + + /* USER CODE END COMP_IRQn 1 */ +} + /** * @brief This function handles EXTI line[9:5] interrupts. */ diff --git a/firmware/targets/f3/f3.ioc b/firmware/targets/f3/f3.ioc index 96ae441e..ffc9f8b5 100644 --- a/firmware/targets/f3/f3.ioc +++ b/firmware/targets/f3/f3.ioc @@ -17,8 +17,10 @@ VP_RTC_VS_RTC_Activate.Mode=RTC_Enabled RCC.RTCFreq_Value=32768 PA3.GPIOParameters=GPIO_Speed,PinState,GPIO_Label,GPIO_ModeDefaultOutputPP PA6.GPIO_Label=PA6 +COMP1.Hysteresis=COMP_HYSTERESIS_HIGH PD0.Locked=true PC5.Mode=INP +VP_COMP1_VS_VREFINT14.Signal=COMP1_VS_VREFINT14 USART1.IPParameters=VirtualMode-Asynchronous PA3.GPIO_Speed=GPIO_SPEED_FREQ_LOW PB13.Signal=TIM1_CH1N @@ -37,6 +39,7 @@ SPI1.Direction=SPI_DIRECTION_2LINES TIM2.IPParameters=Channel-Input_Capture1_from_TI1,ICPolarity_CH1,AutoReloadPreload,Prescaler,Channel-Input_Capture2_from_TI1 RCC.APB2TimFreq_Value=64000000 PCC.Ble.PowerLevel=Min +COMP1.Mode=COMP_POWERMODE_MEDIUMSPEED PB6.Signal=USART1_TX PB6.Mode=Asynchronous SPI1.CalculateBaudRate=4.0 MBits/s @@ -175,7 +178,7 @@ Mcu.Pin50=PB6 Mcu.Pin55=VP_FREERTOS_VS_CMSIS_V2 Mcu.Pin56=VP_HSEM_VS_HSEM Mcu.Pin53=VP_ADC1_Vref_Input -Mcu.Pin54=VP_COMP1_VS_VREFINT12 +Mcu.Pin54=VP_COMP1_VS_VREFINT14 PC6.Locked=true PA9.Signal=I2C1_SCL VP_TIM1_VS_ClockSourceINT.Signal=TIM1_VS_ClockSourceINT @@ -216,6 +219,7 @@ RCC.RNGFreq_Value=32000 PC2.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI VP_ADC1_TempSens_Input.Signal=ADC1_TempSens_Input Mcu.Pin30=PE4 +NVIC.COMP_IRQn=true\:5\:0\:true\:false\:true\:false\:false\:true PA1.GPIO_Label=LED_RED Mcu.Pin33=PB14 Mcu.Pin34=PB15 @@ -312,7 +316,6 @@ PA11.Mode=Device PB0.GPIO_Label=DISPLAY_RST VP_RTC_VS_RTC_Calendar.Signal=RTC_VS_RTC_Calendar PB11.GPIO_PuPd=GPIO_PULLUP -VP_COMP1_VS_VREFINT12.Signal=COMP1_VS_VREFINT12 PC13.GPIO_Label=BUTTON_BACK PB13.GPIO_Label=RFID_OUT PB11.Signal=GPXTI11 @@ -401,7 +404,6 @@ PE4.Signal=GPIO_Output PB0.Locked=true FREERTOS.configTOTAL_HEAP_SIZE=40960 PC14-OSC32_IN.GPIOParameters=GPIO_Label -VP_COMP1_VS_VREFINT12.Mode=VREFINT_12 ProjectManager.ProjectName=f3 RCC.APB3Freq_Value=16000000 PA6.Signal=GPIO_Analog @@ -426,6 +428,7 @@ OSC_IN.GPIO_Label=QUARTZ_32KHZ_IN PC2.GPIO_Label=BUTTON_OK PC14-OSC32_IN.Locked=true PA12.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH +COMP1.TriggerMode=COMP_TRIGGERMODE_IT_RISING_FALLING PB15.Locked=true PB3.Locked=true PB4.Signal=SPI1_MISO @@ -463,6 +466,7 @@ PC12.GPIOParameters=GPIO_Speed,PinState,GPIO_Label USART1.VirtualMode-Asynchronous=VM_ASYNC PA13.Signal=SYS_JTMS-SWDIO FREERTOS.configUSE_IDLE_HOOK=1 +VP_COMP1_VS_VREFINT14.Mode=VREFINT_14 PA9.Mode=I2C TIM1.Channel-Output\ Compare1\ CH1N=TIM_CHANNEL_1 FREERTOS.configRECORD_STACK_HIGH_ADDRESS=1 @@ -492,6 +496,7 @@ TIM16.Period=291 NVIC.SavedSystickIrqHandlerGenerated=true RCC.APB2Freq_Value=64000000 PC11.PinState=GPIO_PIN_SET +COMP1.IPParameters=TriggerMode,Hysteresis,Mode MxCube.Version=6.0.1 VP_TIM2_VS_ClockSourceINT.Mode=Internal PC13.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI