mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-11-23 10:01:58 +03:00
added DigitalSequence and PulseReader (#2070)
* added DigitalSequence to chain multiple DigitalSignals added PulseReader for hardware assisted digital signal sampling * added send_time option to start a signal at a specific DWT->CYCCNT value * fixed linter errors and undone function renaming * fixed renaming * flagged functions in api_symbols.csv * allow gpio field to stay uninitialized in digital_signal_prepare_arr() * fix test cases to match (expected) implementation * pulse_reader: build as static library Signed-off-by: g3gg0.de <git@g3gg0.de> * fix starting level detection in pulse_reader * added unit test for pulse_reader * change pulse reader test timings to 1, 10 and 100 ms * fine tuned timings for pulse_reader test * pulse_reader_stop now deinits GPIO as recommended by @gornekich * ran format_py * pulse_reader: remove from API, allow to link with faps Signed-off-by: g3gg0.de <git@g3gg0.de> * remove unit test for pulse_reader again * pulse_reader: add call to set GPIO pull direction * make structures private, add C implementation of digital_signal_update_dma() * digital_signal/pulse_reader: allow parameters for free to be NULL * digital_signal: show unoptimized and optimized code for digital_signal_update_dma() next to each other * pulse_reader: further optimize assembly code * digital_signal: reduce code complexity of digital_signal_update_dma() by only reconfiguring DMA2 * digital_signal: remove assembly code, limiting the performance but increasing portability * added recovery if the timer already expired * digital_signal: fix memory leak * digital_signal: keep lock until all DMA transfers have finished * DigitalSequence: fix issues with concatenation of same levels and spurious bit flips * DigitalSignal: use cyclic DMA buffer for sequences * update api_symbols.csv * Update api_symbols.csv for f18 target * Patches from @gornekich to fix linter warnings. * Remove some redundant if checks * Remove some magic numbers and reformat. * Remove forced terminating edge. Signed-off-by: g3gg0.de <git@g3gg0.de> Co-authored-by: gornekich <n.gorbadey@gmail.com> Co-authored-by: Tiernan Messmer <tiernan.messmer@gmail.com> Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
parent
f57f0efc48
commit
e1c6e78b2e
@ -3,4 +3,4 @@ Version: 1
|
||||
Data length: 18
|
||||
Plain data: f1 99 41 43 a1 2f 23 01 de f3 c5 8d 91 4b 1e 50 4a c9
|
||||
Timings length: 1304
|
||||
Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 0
|
||||
Timings: 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 640 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 641 37 36 37 37 37 36 37 37 37 36 37 37 37 37 36 641 37 37 36 37 37 37 36 37 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37
|
||||
|
@ -3,4 +3,4 @@ Version: 1
|
||||
Data length: 4
|
||||
Plain data: 14 d8 a0 c9
|
||||
Timings length: 296
|
||||
Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 0
|
||||
Timings: 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 339 37 37 36 37 37 37 36 339 37 37 37 36 37 37 37 338 37 37 37 36 37 37 37 338 37 37 37 37 36 37 37 339 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 641 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 339 37 37 36 37 37 37 37 640 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 339 36 37 37 37 36 37 37 339 37 36 37 37 37 36 37 339 37 36 37 37 37 36 37 339 37 37 36 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 641 36 37 37 37 36 37 37 37 36 37 37 37 36 37 37 641 37 36 37 37 37 36 37 339 37 36 37 37 37 37 36 37 37 37 36 37 37 37 36 641 37 37 36 37 37 37 37 338 37 37 37 36 37 37 37 36 37 37 37 36 37 37 37 339 36 37 37 37 36 37 37 641 36 37 37 37 37 36 37 37
|
||||
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,26.1,,
|
||||
Version,+,26.2,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@ -156,6 +156,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,,
|
||||
Header,+,lib/pulse_reader/pulse_reader.h,,
|
||||
Header,+,lib/toolbox/args.h,,
|
||||
Header,+,lib/toolbox/crc32_calc.h,,
|
||||
Header,+,lib/toolbox/dir_walk.h,,
|
||||
@ -1575,6 +1576,15 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz
|
||||
Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t"
|
||||
Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*"
|
||||
Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t"
|
||||
Function,-,pulse_reader_free,void,PulseReader*
|
||||
Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int"
|
||||
Function,-,pulse_reader_samples,uint32_t,PulseReader*
|
||||
Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t"
|
||||
Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull"
|
||||
Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit"
|
||||
Function,-,pulse_reader_start,void,PulseReader*
|
||||
Function,-,pulse_reader_stop,void,PulseReader*
|
||||
Function,-,putc,int,"int, FILE*"
|
||||
Function,-,putc_unlocked,int,"int, FILE*"
|
||||
Function,-,putchar,int,int
|
||||
|
|
@ -1,5 +1,5 @@
|
||||
entry,status,name,type,params
|
||||
Version,+,26.1,,
|
||||
Version,+,26.2,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
@ -175,6 +175,7 @@ Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_tim.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h,,
|
||||
Header,+,lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h,,
|
||||
Header,+,lib/pulse_reader/pulse_reader.h,,
|
||||
Header,+,lib/subghz/blocks/const.h,,
|
||||
Header,+,lib/subghz/blocks/decoder.h,,
|
||||
Header,+,lib/subghz/blocks/encoder.h,,
|
||||
@ -738,6 +739,16 @@ Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, u
|
||||
Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*"
|
||||
Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*"
|
||||
Function,-,difftime,double,"time_t, time_t"
|
||||
Function,-,digital_sequence_add,void,"DigitalSequence*, uint8_t"
|
||||
Function,-,digital_sequence_alloc,DigitalSequence*,"uint32_t, const GpioPin*"
|
||||
Function,-,digital_sequence_clear,void,DigitalSequence*
|
||||
Function,-,digital_sequence_free,void,DigitalSequence*
|
||||
Function,-,digital_sequence_send,_Bool,DigitalSequence*
|
||||
Function,-,digital_sequence_set_sendtime,void,"DigitalSequence*, uint32_t"
|
||||
Function,-,digital_sequence_set_signal,void,"DigitalSequence*, uint8_t, DigitalSignal*"
|
||||
Function,-,digital_sequence_timebase_correction,void,"DigitalSequence*, float"
|
||||
Function,-,digital_signal_add,void,"DigitalSignal*, uint32_t"
|
||||
Function,-,digital_signal_add_pulse,void,"DigitalSignal*, uint32_t, _Bool"
|
||||
Function,-,digital_signal_alloc,DigitalSignal*,uint32_t
|
||||
Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*"
|
||||
Function,-,digital_signal_free,void,DigitalSignal*
|
||||
@ -2168,6 +2179,15 @@ Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, siz
|
||||
Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t"
|
||||
Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t"
|
||||
Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*"
|
||||
Function,-,pulse_reader_alloc,PulseReader*,"const GpioPin*, uint32_t"
|
||||
Function,-,pulse_reader_free,void,PulseReader*
|
||||
Function,-,pulse_reader_receive,uint32_t,"PulseReader*, int"
|
||||
Function,-,pulse_reader_samples,uint32_t,PulseReader*
|
||||
Function,-,pulse_reader_set_bittime,void,"PulseReader*, uint32_t"
|
||||
Function,-,pulse_reader_set_pull,void,"PulseReader*, GpioPull"
|
||||
Function,-,pulse_reader_set_timebase,void,"PulseReader*, PulseReaderUnit"
|
||||
Function,-,pulse_reader_start,void,PulseReader*
|
||||
Function,-,pulse_reader_stop,void,PulseReader*
|
||||
Function,-,putc,int,"int, FILE*"
|
||||
Function,-,putc_unlocked,int,"int, FILE*"
|
||||
Function,-,putchar,int,int
|
||||
|
|
@ -28,6 +28,7 @@
|
||||
"flipperformat",
|
||||
"toolbox",
|
||||
"nfc",
|
||||
"pulse_reader",
|
||||
"microtar",
|
||||
"usb_stm32",
|
||||
"st25rfal002",
|
||||
|
@ -4,6 +4,7 @@ env.Append(
|
||||
LINT_SOURCES=[
|
||||
Dir("app-scened-template"),
|
||||
Dir("digital_signal"),
|
||||
Dir("pulse_reader"),
|
||||
Dir("drivers"),
|
||||
Dir("flipper_format"),
|
||||
Dir("infrared"),
|
||||
@ -14,6 +15,7 @@ env.Append(
|
||||
Dir("u8g2"),
|
||||
Dir("update_util"),
|
||||
Dir("print"),
|
||||
Dir("pulse_reader"),
|
||||
],
|
||||
)
|
||||
|
||||
@ -93,6 +95,7 @@ libs = env.BuildModules(
|
||||
"mbedtls",
|
||||
"subghz",
|
||||
"nfc",
|
||||
"pulse_reader",
|
||||
"appframe",
|
||||
"misc",
|
||||
"lfrfid",
|
||||
|
@ -1,23 +1,98 @@
|
||||
#include "digital_signal.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_resources.h>
|
||||
#include <math.h>
|
||||
|
||||
#pragma GCC optimize("O3,unroll-loops,Ofast")
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
/* must be on bank B */
|
||||
#define DEBUG_OUTPUT gpio_ext_pb3
|
||||
|
||||
struct ReloadBuffer {
|
||||
uint32_t* buffer; /* DMA ringbuffer */
|
||||
uint32_t size; /* maximum entry count of the ring buffer */
|
||||
uint32_t write_pos; /* current buffer write index */
|
||||
uint32_t read_pos; /* current buffer read index */
|
||||
bool dma_active;
|
||||
};
|
||||
|
||||
struct DigitalSequence {
|
||||
uint8_t signals_size;
|
||||
bool bake;
|
||||
uint32_t sequence_used;
|
||||
uint32_t sequence_size;
|
||||
DigitalSignal** signals;
|
||||
uint8_t* sequence;
|
||||
const GpioPin* gpio;
|
||||
uint32_t send_time;
|
||||
bool send_time_active;
|
||||
LL_DMA_InitTypeDef dma_config_gpio;
|
||||
LL_DMA_InitTypeDef dma_config_timer;
|
||||
uint32_t* gpio_buff;
|
||||
struct ReloadBuffer* dma_buffer;
|
||||
};
|
||||
|
||||
struct DigitalSignalInternals {
|
||||
uint64_t factor;
|
||||
uint32_t reload_reg_entries;
|
||||
uint32_t reload_reg_remainder;
|
||||
uint32_t gpio_buff[2];
|
||||
const GpioPin* gpio;
|
||||
LL_DMA_InitTypeDef dma_config_gpio;
|
||||
LL_DMA_InitTypeDef dma_config_timer;
|
||||
};
|
||||
|
||||
#define TAG "DigitalSignal"
|
||||
|
||||
#define F_TIM (64000000.0)
|
||||
#define T_TIM 1562 //15.625 ns *100
|
||||
#define T_TIM_DIV2 781 //15.625 ns / 2 *100
|
||||
#define T_TIM 1562 /* 15.625 ns *100 */
|
||||
#define T_TIM_DIV2 781 /* 15.625 ns / 2 *100 */
|
||||
|
||||
/* maximum entry count of the sequence dma ring buffer */
|
||||
#define SEQUENCE_DMA_RINGBUFFER_SIZE 32
|
||||
/* maximum number of DigitalSignals in a sequence */
|
||||
#define SEQUENCE_SIGNALS_SIZE 32
|
||||
/*
|
||||
* if sequence size runs out from the initial value passed to digital_sequence_alloc
|
||||
* the size will be increased by this amount and reallocated
|
||||
*/
|
||||
#define SEQUENCE_SIZE_REALLOCATE_INCREMENT 256
|
||||
|
||||
DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt) {
|
||||
DigitalSignal* signal = malloc(sizeof(DigitalSignal));
|
||||
signal->start_level = true;
|
||||
signal->edges_max_cnt = max_edges_cnt;
|
||||
signal->edge_timings = malloc(max_edges_cnt * sizeof(uint32_t));
|
||||
signal->reload_reg_buff = malloc(max_edges_cnt * sizeof(uint32_t));
|
||||
signal->edge_timings = malloc(signal->edges_max_cnt * sizeof(uint32_t));
|
||||
signal->edge_cnt = 0;
|
||||
signal->reload_reg_buff = malloc(signal->edges_max_cnt * sizeof(uint32_t));
|
||||
|
||||
signal->internals = malloc(sizeof(DigitalSignalInternals));
|
||||
DigitalSignalInternals* internals = signal->internals;
|
||||
|
||||
internals->factor = 1024 * 1024;
|
||||
|
||||
internals->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
internals->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
internals->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
internals->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
internals->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
internals->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
internals->dma_config_gpio.NbData = 2;
|
||||
internals->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
internals->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
|
||||
internals->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR);
|
||||
internals->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
internals->dma_config_timer.Mode = LL_DMA_MODE_NORMAL;
|
||||
internals->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
internals->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
internals->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
internals->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
internals->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
internals->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;
|
||||
|
||||
return signal;
|
||||
}
|
||||
@ -27,6 +102,7 @@ void digital_signal_free(DigitalSignal* signal) {
|
||||
|
||||
free(signal->edge_timings);
|
||||
free(signal->reload_reg_buff);
|
||||
free(signal->internals);
|
||||
free(signal);
|
||||
}
|
||||
|
||||
@ -37,7 +113,10 @@ bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b) {
|
||||
if(signal_a->edges_max_cnt < signal_a->edge_cnt + signal_b->edge_cnt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* in case there are no edges in our target signal, the signal to append makes the rules */
|
||||
if(!signal_a->edge_cnt) {
|
||||
signal_a->start_level = signal_b->start_level;
|
||||
}
|
||||
bool end_level = signal_a->start_level;
|
||||
if(signal_a->edge_cnt) {
|
||||
end_level = signal_a->start_level ^ !(signal_a->edge_cnt % 2);
|
||||
@ -72,6 +151,32 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal) {
|
||||
return signal->edge_cnt;
|
||||
}
|
||||
|
||||
void digital_signal_add(DigitalSignal* signal, uint32_t ticks) {
|
||||
furi_assert(signal);
|
||||
furi_assert(signal->edge_cnt < signal->edges_max_cnt);
|
||||
|
||||
signal->edge_timings[signal->edge_cnt++] = ticks;
|
||||
}
|
||||
|
||||
void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level) {
|
||||
furi_assert(signal);
|
||||
furi_assert(signal->edge_cnt < signal->edges_max_cnt);
|
||||
|
||||
/* virgin signal? add it as the only level */
|
||||
if(signal->edge_cnt == 0) {
|
||||
signal->start_level = level;
|
||||
signal->edge_timings[signal->edge_cnt++] = ticks;
|
||||
} else {
|
||||
bool end_level = signal->start_level ^ !(signal->edge_cnt % 2);
|
||||
|
||||
if(level != end_level) {
|
||||
signal->edge_timings[signal->edge_cnt++] = ticks;
|
||||
} else {
|
||||
signal->edge_timings[signal->edge_cnt - 1] += ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) {
|
||||
furi_assert(signal);
|
||||
furi_assert(edge_num < signal->edge_cnt);
|
||||
@ -80,94 +185,473 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num) {
|
||||
}
|
||||
|
||||
void digital_signal_prepare_arr(DigitalSignal* signal) {
|
||||
uint32_t t_signal_rest = signal->edge_timings[0];
|
||||
uint32_t r_count_tick_arr = 0;
|
||||
uint32_t r_rest_div = 0;
|
||||
furi_assert(signal);
|
||||
|
||||
for(size_t i = 0; i < signal->edge_cnt - 1; i++) {
|
||||
r_count_tick_arr = t_signal_rest / T_TIM;
|
||||
r_rest_div = t_signal_rest % T_TIM;
|
||||
t_signal_rest = signal->edge_timings[i + 1] + r_rest_div;
|
||||
DigitalSignalInternals* internals = signal->internals;
|
||||
|
||||
if(r_rest_div < T_TIM_DIV2) {
|
||||
signal->reload_reg_buff[i] = r_count_tick_arr - 1;
|
||||
/* set up signal polarities */
|
||||
if(internals->gpio) {
|
||||
uint32_t bit_set = internals->gpio->pin;
|
||||
uint32_t bit_reset = internals->gpio->pin << 16;
|
||||
|
||||
#ifdef DEBUG_OUTPUT
|
||||
bit_set |= DEBUG_OUTPUT.pin;
|
||||
bit_reset |= DEBUG_OUTPUT.pin << 16;
|
||||
#endif
|
||||
|
||||
if(signal->start_level) {
|
||||
internals->gpio_buff[0] = bit_set;
|
||||
internals->gpio_buff[1] = bit_reset;
|
||||
} else {
|
||||
signal->reload_reg_buff[i] = r_count_tick_arr;
|
||||
t_signal_rest -= T_TIM;
|
||||
internals->gpio_buff[0] = bit_reset;
|
||||
internals->gpio_buff[1] = bit_set;
|
||||
}
|
||||
}
|
||||
|
||||
/* set up edge timings */
|
||||
internals->reload_reg_entries = 0;
|
||||
|
||||
for(size_t pos = 0; pos < signal->edge_cnt; pos++) {
|
||||
uint32_t edge_scaled = (internals->factor * signal->edge_timings[pos]) / (1024 * 1024);
|
||||
uint32_t pulse_duration = edge_scaled + internals->reload_reg_remainder;
|
||||
if(pulse_duration < 10 || pulse_duration > 10000000) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"[prepare] pulse_duration out of range: %lu = %lu * %llu",
|
||||
pulse_duration,
|
||||
signal->edge_timings[pos],
|
||||
internals->factor);
|
||||
pulse_duration = 100;
|
||||
}
|
||||
uint32_t pulse_ticks = (pulse_duration + T_TIM_DIV2) / T_TIM;
|
||||
internals->reload_reg_remainder = pulse_duration - (pulse_ticks * T_TIM);
|
||||
|
||||
if(pulse_ticks > 1) {
|
||||
signal->reload_reg_buff[internals->reload_reg_entries++] = pulse_ticks - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) {
|
||||
furi_assert(signal);
|
||||
furi_assert(gpio);
|
||||
static void digital_signal_stop_dma() {
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
LL_DMA_ClearFlag_TC2(DMA1);
|
||||
}
|
||||
|
||||
// Configure gpio as output
|
||||
furi_hal_gpio_init(gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
static void digital_signal_stop_timer() {
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
LL_TIM_DisableUpdateEvent(TIM2);
|
||||
LL_TIM_DisableDMAReq_UPDATE(TIM2);
|
||||
}
|
||||
|
||||
// Init gpio buffer and DMA channel
|
||||
uint16_t gpio_reg = gpio->port->ODR;
|
||||
uint16_t gpio_buff[2];
|
||||
if(signal->start_level) {
|
||||
gpio_buff[0] = gpio_reg | gpio->pin;
|
||||
gpio_buff[1] = gpio_reg & ~(gpio->pin);
|
||||
} else {
|
||||
gpio_buff[0] = gpio_reg & ~(gpio->pin);
|
||||
gpio_buff[1] = gpio_reg | gpio->pin;
|
||||
}
|
||||
LL_DMA_InitTypeDef dma_config = {};
|
||||
dma_config.MemoryOrM2MDstAddress = (uint32_t)gpio_buff;
|
||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->ODR);
|
||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
dma_config.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
|
||||
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
|
||||
dma_config.NbData = 2;
|
||||
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
|
||||
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, 2);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
static void digital_signal_setup_timer() {
|
||||
digital_signal_stop_timer();
|
||||
|
||||
// Init timer arr register buffer and DMA channel
|
||||
digital_signal_prepare_arr(signal);
|
||||
dma_config.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff;
|
||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR);
|
||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
||||
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
dma_config.NbData = signal->edge_cnt - 2;
|
||||
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
dma_config.Priority = LL_DMA_PRIORITY_HIGH;
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
|
||||
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, signal->edge_cnt - 2);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
// Set up timer
|
||||
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
LL_TIM_SetPrescaler(TIM2, 0);
|
||||
LL_TIM_SetAutoReload(TIM2, 10);
|
||||
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
}
|
||||
|
||||
static void digital_signal_start_timer() {
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
LL_TIM_EnableUpdateEvent(TIM2);
|
||||
LL_TIM_EnableDMAReq_UPDATE(TIM2);
|
||||
|
||||
// Start transactions
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM2); // Do we really need it?
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
|
||||
while(!LL_DMA_IsActiveFlag_TC2(DMA1))
|
||||
;
|
||||
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
LL_DMA_ClearFlag_TC2(DMA1);
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM2);
|
||||
}
|
||||
|
||||
static bool digital_signal_setup_dma(DigitalSignal* signal) {
|
||||
furi_assert(signal);
|
||||
DigitalSignalInternals* internals = signal->internals;
|
||||
|
||||
if(!signal->internals->reload_reg_entries) {
|
||||
return false;
|
||||
}
|
||||
digital_signal_stop_dma();
|
||||
|
||||
internals->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)internals->gpio_buff;
|
||||
internals->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (internals->gpio->port->BSRR);
|
||||
internals->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->reload_reg_buff;
|
||||
internals->dma_config_timer.NbData = signal->internals->reload_reg_entries;
|
||||
|
||||
/* set up DMA channel 1 and 2 for GPIO and timer copy operations */
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &internals->dma_config_gpio);
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &internals->dma_config_timer);
|
||||
|
||||
/* enable both DMA channels */
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio) {
|
||||
furi_assert(signal);
|
||||
|
||||
if(!signal->edge_cnt) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Configure gpio as output */
|
||||
signal->internals->gpio = gpio;
|
||||
furi_hal_gpio_init(
|
||||
signal->internals->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
|
||||
digital_signal_prepare_arr(signal);
|
||||
|
||||
digital_signal_setup_dma(signal);
|
||||
digital_signal_setup_timer();
|
||||
digital_signal_start_timer();
|
||||
|
||||
while(!LL_DMA_IsActiveFlag_TC2(DMA1)) {
|
||||
}
|
||||
|
||||
digital_signal_stop_timer();
|
||||
digital_signal_stop_dma();
|
||||
}
|
||||
|
||||
static void digital_sequence_alloc_signals(DigitalSequence* sequence, uint32_t size) {
|
||||
sequence->signals_size = size;
|
||||
sequence->signals = malloc(sequence->signals_size * sizeof(DigitalSignal*));
|
||||
}
|
||||
|
||||
static void digital_sequence_alloc_sequence(DigitalSequence* sequence, uint32_t size) {
|
||||
sequence->sequence_used = 0;
|
||||
sequence->sequence_size = size;
|
||||
sequence->sequence = malloc(sequence->sequence_size);
|
||||
sequence->send_time = 0;
|
||||
sequence->send_time_active = false;
|
||||
}
|
||||
|
||||
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio) {
|
||||
furi_assert(gpio);
|
||||
|
||||
DigitalSequence* sequence = malloc(sizeof(DigitalSequence));
|
||||
|
||||
sequence->gpio = gpio;
|
||||
sequence->bake = false;
|
||||
|
||||
sequence->dma_buffer = malloc(sizeof(struct ReloadBuffer));
|
||||
sequence->dma_buffer->size = SEQUENCE_DMA_RINGBUFFER_SIZE;
|
||||
sequence->dma_buffer->buffer = malloc(sequence->dma_buffer->size * sizeof(uint32_t));
|
||||
|
||||
sequence->dma_config_gpio.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
sequence->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
sequence->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
sequence->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
sequence->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
sequence->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
sequence->dma_config_gpio.NbData = 2;
|
||||
sequence->dma_config_gpio.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
sequence->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
|
||||
sequence->dma_config_timer.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
sequence->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
sequence->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
sequence->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
sequence->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
sequence->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
sequence->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->ARR);
|
||||
sequence->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)sequence->dma_buffer->buffer;
|
||||
sequence->dma_config_timer.NbData = sequence->dma_buffer->size;
|
||||
sequence->dma_config_timer.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
sequence->dma_config_timer.Priority = LL_DMA_PRIORITY_HIGH;
|
||||
|
||||
digital_sequence_alloc_signals(sequence, SEQUENCE_SIGNALS_SIZE);
|
||||
digital_sequence_alloc_sequence(sequence, size);
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
void digital_sequence_free(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
free(sequence->signals);
|
||||
free(sequence->sequence);
|
||||
free(sequence->dma_buffer->buffer);
|
||||
free(sequence->dma_buffer);
|
||||
free(sequence);
|
||||
}
|
||||
|
||||
void digital_sequence_set_signal(
|
||||
DigitalSequence* sequence,
|
||||
uint8_t signal_index,
|
||||
DigitalSignal* signal) {
|
||||
furi_assert(sequence);
|
||||
furi_assert(signal);
|
||||
furi_assert(signal_index < sequence->signals_size);
|
||||
|
||||
sequence->signals[signal_index] = signal;
|
||||
signal->internals->gpio = sequence->gpio;
|
||||
signal->internals->reload_reg_remainder = 0;
|
||||
|
||||
digital_signal_prepare_arr(signal);
|
||||
}
|
||||
|
||||
void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time) {
|
||||
furi_assert(sequence);
|
||||
|
||||
sequence->send_time = send_time;
|
||||
sequence->send_time_active = true;
|
||||
}
|
||||
|
||||
void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index) {
|
||||
furi_assert(sequence);
|
||||
furi_assert(signal_index < sequence->signals_size);
|
||||
|
||||
if(sequence->sequence_used >= sequence->sequence_size) {
|
||||
sequence->sequence_size += SEQUENCE_SIZE_REALLOCATE_INCREMENT;
|
||||
sequence->sequence = realloc(sequence->sequence, sequence->sequence_size); //-V701
|
||||
furi_assert(sequence->sequence);
|
||||
}
|
||||
|
||||
sequence->sequence[sequence->sequence_used++] = signal_index;
|
||||
}
|
||||
|
||||
static bool digital_sequence_setup_dma(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
digital_signal_stop_dma();
|
||||
|
||||
sequence->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)sequence->gpio_buff;
|
||||
sequence->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (sequence->gpio->port->BSRR);
|
||||
|
||||
/* set up DMA channel 1 and 2 for GPIO and timer copy operations */
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &sequence->dma_config_gpio);
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &sequence->dma_config_timer);
|
||||
|
||||
/* enable both DMA channels */
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DigitalSignal* digital_sequence_bake(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
uint32_t edges = 0;
|
||||
|
||||
for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) {
|
||||
uint8_t signal_index = sequence->sequence[pos];
|
||||
DigitalSignal* sig = sequence->signals[signal_index];
|
||||
|
||||
edges += sig->edge_cnt;
|
||||
}
|
||||
|
||||
DigitalSignal* ret = digital_signal_alloc(edges);
|
||||
|
||||
for(uint32_t pos = 0; pos < sequence->sequence_used; pos++) {
|
||||
uint8_t signal_index = sequence->sequence[pos];
|
||||
DigitalSignal* sig = sequence->signals[signal_index];
|
||||
|
||||
digital_signal_append(ret, sig);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void digital_sequence_update_pos(DigitalSequence* sequence) {
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
dma_buffer->read_pos = dma_buffer->size - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_2);
|
||||
}
|
||||
|
||||
static const uint32_t wait_ms = 10;
|
||||
static const uint32_t wait_ticks = wait_ms * 1000 * 64;
|
||||
|
||||
static void digital_sequence_finish(DigitalSequence* sequence) {
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
if(dma_buffer->dma_active) {
|
||||
uint32_t prev_timer = DWT->CYCCNT;
|
||||
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
do {
|
||||
uint32_t last_pos = dma_buffer->read_pos;
|
||||
|
||||
digital_sequence_update_pos(sequence);
|
||||
|
||||
/* we are finished, when the DMA transferred the 0xFFFFFFFF-timer which is the current write_pos */
|
||||
if(dma_buffer->read_pos == end_pos) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(last_pos != dma_buffer->read_pos) { //-V547
|
||||
prev_timer = DWT->CYCCNT;
|
||||
}
|
||||
if(DWT->CYCCNT - prev_timer > wait_ticks) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"[SEQ] hung %lu ms in finish (ARR 0x%08lx, read %lu, write %lu)",
|
||||
wait_ms,
|
||||
TIM2->ARR,
|
||||
dma_buffer->read_pos,
|
||||
dma_buffer->write_pos);
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
|
||||
digital_signal_stop_timer();
|
||||
digital_signal_stop_dma();
|
||||
}
|
||||
|
||||
static void digital_sequence_queue_pulse(DigitalSequence* sequence, uint32_t length) {
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
if(dma_buffer->dma_active) {
|
||||
uint32_t prev_timer = DWT->CYCCNT;
|
||||
uint32_t end_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
do {
|
||||
uint32_t last_pos = dma_buffer->read_pos;
|
||||
digital_sequence_update_pos(sequence);
|
||||
|
||||
if(dma_buffer->read_pos != end_pos) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(last_pos != dma_buffer->read_pos) { //-V547
|
||||
prev_timer = DWT->CYCCNT;
|
||||
}
|
||||
if(DWT->CYCCNT - prev_timer > wait_ticks) {
|
||||
FURI_LOG_D(
|
||||
TAG,
|
||||
"[SEQ] hung %lu ms in queue (ARR 0x%08lx, read %lu, write %lu)",
|
||||
wait_ms,
|
||||
TIM2->ARR,
|
||||
dma_buffer->read_pos,
|
||||
dma_buffer->write_pos);
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
|
||||
dma_buffer->buffer[dma_buffer->write_pos] = length;
|
||||
dma_buffer->write_pos = (dma_buffer->write_pos + 1) % dma_buffer->size;
|
||||
dma_buffer->buffer[dma_buffer->write_pos] = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
bool digital_sequence_send(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
struct ReloadBuffer* dma_buffer = sequence->dma_buffer;
|
||||
|
||||
furi_hal_gpio_init(sequence->gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#ifdef DEBUG_OUTPUT
|
||||
furi_hal_gpio_init(&DEBUG_OUTPUT, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
|
||||
#endif
|
||||
|
||||
if(sequence->bake) {
|
||||
DigitalSignal* sig = digital_sequence_bake(sequence);
|
||||
|
||||
digital_signal_send(sig, sequence->gpio);
|
||||
digital_signal_free(sig);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t remainder = 0;
|
||||
bool traded_first = false;
|
||||
|
||||
FURI_CRITICAL_ENTER();
|
||||
|
||||
dma_buffer->dma_active = false;
|
||||
dma_buffer->buffer[0] = 0xFFFFFFFF;
|
||||
dma_buffer->read_pos = 0;
|
||||
dma_buffer->write_pos = 0;
|
||||
|
||||
for(uint32_t seq_pos = 0; seq_pos < sequence->sequence_used; seq_pos++) {
|
||||
uint8_t signal_index = sequence->sequence[seq_pos];
|
||||
DigitalSignal* sig = sequence->signals[signal_index];
|
||||
bool last_signal = ((seq_pos + 1) == sequence->sequence_used);
|
||||
|
||||
/* all signals are prepared and we can re-use the GPIO buffer from the fist signal */
|
||||
if(seq_pos == 0) {
|
||||
sequence->gpio_buff = sig->internals->gpio_buff;
|
||||
}
|
||||
|
||||
for(uint32_t pulse_pos = 0; pulse_pos < sig->internals->reload_reg_entries; pulse_pos++) {
|
||||
if(traded_first) {
|
||||
traded_first = false;
|
||||
continue;
|
||||
}
|
||||
uint32_t pulse_length = 0;
|
||||
bool last_pulse = ((pulse_pos + 1) == sig->internals->reload_reg_entries);
|
||||
|
||||
pulse_length = sig->reload_reg_buff[pulse_pos];
|
||||
|
||||
/* when we are too late more than half a tick, make the first edge temporarily longer */
|
||||
if(remainder >= T_TIM_DIV2) {
|
||||
remainder -= T_TIM;
|
||||
pulse_length += 1;
|
||||
}
|
||||
remainder += sig->internals->reload_reg_remainder;
|
||||
|
||||
/* last pulse in that signal and have a next signal? */
|
||||
if(last_pulse) {
|
||||
if((seq_pos + 1) < sequence->sequence_used) {
|
||||
DigitalSignal* sig_next = sequence->signals[sequence->sequence[seq_pos + 1]];
|
||||
|
||||
/* when a signal ends with the same level as the next signal begins, let the fist signal generate the whole pulse */
|
||||
/* beware, we do not want the level after the last edge, but the last level before that edge */
|
||||
bool end_level = sig->start_level ^ ((sig->edge_cnt % 2) == 0);
|
||||
|
||||
/* take from the next, add it to the current if they have the same level */
|
||||
if(end_level == sig_next->start_level) {
|
||||
pulse_length += sig_next->reload_reg_buff[0];
|
||||
traded_first = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
digital_sequence_queue_pulse(sequence, pulse_length);
|
||||
|
||||
/* start transmission when buffer was filled enough */
|
||||
bool start_send = sequence->dma_buffer->write_pos >= (sequence->dma_buffer->size - 4);
|
||||
|
||||
/* or it was the last pulse */
|
||||
if(last_pulse && last_signal) {
|
||||
start_send = true;
|
||||
}
|
||||
|
||||
/* start transmission */
|
||||
if(start_send && !dma_buffer->dma_active) {
|
||||
digital_sequence_setup_dma(sequence);
|
||||
digital_signal_setup_timer();
|
||||
|
||||
/* if the send time is specified, wait till the core timer passed beyond that time */
|
||||
if(sequence->send_time_active) {
|
||||
sequence->send_time_active = false;
|
||||
while(sequence->send_time - DWT->CYCCNT < 0x80000000) {
|
||||
}
|
||||
}
|
||||
digital_signal_start_timer();
|
||||
dma_buffer->dma_active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* wait until last dma transaction was finished */
|
||||
digital_sequence_finish(sequence);
|
||||
FURI_CRITICAL_EXIT();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void digital_sequence_clear(DigitalSequence* sequence) {
|
||||
furi_assert(sequence);
|
||||
|
||||
sequence->sequence_used = 0;
|
||||
}
|
||||
|
||||
void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor) {
|
||||
for(uint32_t sig_pos = 0; sig_pos < sequence->signals_size; sig_pos++) {
|
||||
DigitalSignal* signal = sequence->signals[sig_pos];
|
||||
|
||||
if(signal) {
|
||||
signal->internals->factor = (uint32_t)(1024 * 1024 * factor);
|
||||
digital_signal_prepare_arr(signal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,18 +10,35 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/* helper for easier signal generation */
|
||||
#define DIGITAL_SIGNAL_MS(x) ((x)*100000000UL)
|
||||
#define DIGITAL_SIGNAL_US(x) ((x)*100000UL)
|
||||
#define DIGITAL_SIGNAL_NS(x) ((x)*100UL)
|
||||
#define DIGITAL_SIGNAL_PS(x) ((x) / 10UL)
|
||||
|
||||
/* using an anonymous type for the internals */
|
||||
typedef struct DigitalSignalInternals DigitalSignalInternals;
|
||||
|
||||
/* and a public one for accessing user-side fields */
|
||||
typedef struct DigitalSignal {
|
||||
bool start_level;
|
||||
uint32_t edge_cnt;
|
||||
uint32_t edges_max_cnt;
|
||||
uint32_t* edge_timings;
|
||||
uint32_t* reload_reg_buff;
|
||||
uint32_t* reload_reg_buff; /* internal, but used by unit tests */
|
||||
DigitalSignalInternals* internals;
|
||||
} DigitalSignal;
|
||||
|
||||
typedef struct DigitalSequence DigitalSequence;
|
||||
|
||||
DigitalSignal* digital_signal_alloc(uint32_t max_edges_cnt);
|
||||
|
||||
void digital_signal_free(DigitalSignal* signal);
|
||||
|
||||
void digital_signal_add(DigitalSignal* signal, uint32_t ticks);
|
||||
|
||||
void digital_signal_add_pulse(DigitalSignal* signal, uint32_t ticks, bool level);
|
||||
|
||||
bool digital_signal_append(DigitalSignal* signal_a, DigitalSignal* signal_b);
|
||||
|
||||
void digital_signal_prepare_arr(DigitalSignal* signal);
|
||||
@ -34,6 +51,25 @@ uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num);
|
||||
|
||||
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio);
|
||||
|
||||
DigitalSequence* digital_sequence_alloc(uint32_t size, const GpioPin* gpio);
|
||||
|
||||
void digital_sequence_free(DigitalSequence* sequence);
|
||||
|
||||
void digital_sequence_set_signal(
|
||||
DigitalSequence* sequence,
|
||||
uint8_t signal_index,
|
||||
DigitalSignal* signal);
|
||||
|
||||
void digital_sequence_set_sendtime(DigitalSequence* sequence, uint32_t send_time);
|
||||
|
||||
void digital_sequence_add(DigitalSequence* sequence, uint8_t signal_index);
|
||||
|
||||
bool digital_sequence_send(DigitalSequence* sequence);
|
||||
|
||||
void digital_sequence_clear(DigitalSequence* sequence);
|
||||
|
||||
void digital_sequence_timebase_correction(DigitalSequence* sequence, float factor);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
27
lib/pulse_reader/SConscript
Normal file
27
lib/pulse_reader/SConscript
Normal file
@ -0,0 +1,27 @@
|
||||
Import("env")
|
||||
|
||||
env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/pulse_reader",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("pulse_reader.h"),
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="pulse_reader")
|
||||
libenv.ApplyLibFlags()
|
||||
|
||||
libenv.AppendUnique(
|
||||
CCFLAGS=[
|
||||
# Required for lib to be linkable with .faps
|
||||
"-mword-relocations",
|
||||
"-mlong-calls",
|
||||
],
|
||||
)
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*")
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
233
lib/pulse_reader/pulse_reader.c
Normal file
233
lib/pulse_reader/pulse_reader.c
Normal file
@ -0,0 +1,233 @@
|
||||
#include "pulse_reader.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#include <stm32wbxx_ll_dma.h>
|
||||
#include <stm32wbxx_ll_dmamux.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <stm32wbxx_ll_exti.h>
|
||||
|
||||
struct PulseReader {
|
||||
uint32_t* timer_buffer;
|
||||
uint32_t* gpio_buffer;
|
||||
uint32_t size;
|
||||
uint32_t pos;
|
||||
uint32_t timer_value;
|
||||
uint32_t gpio_value;
|
||||
uint32_t gpio_mask;
|
||||
uint32_t unit_multiplier;
|
||||
uint32_t unit_divider;
|
||||
uint32_t bit_time;
|
||||
uint32_t dma_channel;
|
||||
const GpioPin* gpio;
|
||||
GpioPull pull;
|
||||
LL_DMA_InitTypeDef dma_config_timer;
|
||||
LL_DMA_InitTypeDef dma_config_gpio;
|
||||
};
|
||||
|
||||
#define GPIO_PIN_MAP(pin, prefix) \
|
||||
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
|
||||
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
|
||||
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
|
||||
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
|
||||
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
|
||||
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
|
||||
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
|
||||
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
|
||||
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
|
||||
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
|
||||
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
|
||||
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
|
||||
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
|
||||
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
|
||||
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
|
||||
prefix##15)
|
||||
|
||||
#define GET_DMAMUX_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_DMAMUX_REQ_GEN_EXTI_LINE)
|
||||
|
||||
PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size) {
|
||||
PulseReader* signal = malloc(sizeof(PulseReader));
|
||||
signal->timer_buffer = malloc(size * sizeof(uint32_t));
|
||||
signal->gpio_buffer = malloc(size * sizeof(uint32_t));
|
||||
signal->dma_channel = LL_DMA_CHANNEL_4;
|
||||
signal->gpio = gpio;
|
||||
signal->pull = GpioPullNo;
|
||||
signal->size = size;
|
||||
signal->timer_value = 0;
|
||||
signal->pos = 0;
|
||||
|
||||
pulse_reader_set_timebase(signal, PulseReaderUnit64MHz);
|
||||
pulse_reader_set_bittime(signal, 1);
|
||||
|
||||
signal->dma_config_timer.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
|
||||
signal->dma_config_timer.PeriphOrM2MSrcAddress = (uint32_t) & (TIM2->CNT);
|
||||
signal->dma_config_timer.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
signal->dma_config_timer.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
signal->dma_config_timer.MemoryOrM2MDstAddress = (uint32_t)signal->timer_buffer;
|
||||
signal->dma_config_timer.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
signal->dma_config_timer.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
signal->dma_config_timer.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
signal->dma_config_timer.PeriphRequest =
|
||||
LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
|
||||
signal->dma_config_timer.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
|
||||
signal->dma_config_gpio.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
|
||||
signal->dma_config_gpio.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
signal->dma_config_gpio.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
signal->dma_config_gpio.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
signal->dma_config_gpio.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
signal->dma_config_gpio.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
signal->dma_config_gpio.PeriphRequest =
|
||||
LL_DMAMUX_REQ_GENERATOR0; /* executes LL_DMA_SetPeriphRequest */
|
||||
signal->dma_config_gpio.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit) {
|
||||
switch(unit) {
|
||||
case PulseReaderUnit64MHz:
|
||||
signal->unit_multiplier = 1;
|
||||
signal->unit_divider = 1;
|
||||
break;
|
||||
case PulseReaderUnitPicosecond:
|
||||
signal->unit_multiplier = 15625;
|
||||
signal->unit_divider = 1;
|
||||
break;
|
||||
case PulseReaderUnitNanosecond:
|
||||
signal->unit_multiplier = 15625;
|
||||
signal->unit_divider = 1000;
|
||||
break;
|
||||
case PulseReaderUnitMicrosecond:
|
||||
signal->unit_multiplier = 15625;
|
||||
signal->unit_divider = 1000000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time) {
|
||||
signal->bit_time = bit_time;
|
||||
}
|
||||
|
||||
void pulse_reader_set_pull(PulseReader* signal, GpioPull pull) {
|
||||
signal->pull = pull;
|
||||
}
|
||||
|
||||
void pulse_reader_free(PulseReader* signal) {
|
||||
furi_assert(signal);
|
||||
|
||||
free(signal->timer_buffer);
|
||||
free(signal->gpio_buffer);
|
||||
free(signal);
|
||||
}
|
||||
|
||||
uint32_t pulse_reader_samples(PulseReader* signal) {
|
||||
uint32_t dma_pos = signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
|
||||
|
||||
return ((signal->pos + signal->size) - dma_pos) % signal->size;
|
||||
}
|
||||
|
||||
void pulse_reader_stop(PulseReader* signal) {
|
||||
LL_DMA_DisableChannel(DMA1, signal->dma_channel);
|
||||
LL_DMA_DisableChannel(DMA1, signal->dma_channel + 1);
|
||||
LL_DMAMUX_DisableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
furi_hal_gpio_init_simple(signal->gpio, GpioModeAnalog);
|
||||
}
|
||||
|
||||
void pulse_reader_start(PulseReader* signal) {
|
||||
/* configure DMA to read from a timer peripheral */
|
||||
signal->dma_config_timer.NbData = signal->size;
|
||||
|
||||
signal->dma_config_gpio.PeriphOrM2MSrcAddress = (uint32_t) & (signal->gpio->port->IDR);
|
||||
signal->dma_config_gpio.MemoryOrM2MDstAddress = (uint32_t)signal->gpio_buffer;
|
||||
signal->dma_config_gpio.NbData = signal->size;
|
||||
|
||||
/* start counter */
|
||||
LL_TIM_SetCounterMode(TIM2, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_SetClockDivision(TIM2, LL_TIM_CLOCKDIVISION_DIV1);
|
||||
LL_TIM_SetPrescaler(TIM2, 0);
|
||||
LL_TIM_SetAutoReload(TIM2, 0xFFFFFFFF);
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
|
||||
/* generator 0 gets fed by EXTI_LINEn */
|
||||
LL_DMAMUX_SetRequestSignalID(
|
||||
NULL, LL_DMAMUX_REQ_GEN_0, GET_DMAMUX_EXTI_LINE(signal->gpio->pin));
|
||||
/* trigger on rising edge of the interrupt */
|
||||
LL_DMAMUX_SetRequestGenPolarity(NULL, LL_DMAMUX_REQ_GEN_0, LL_DMAMUX_REQ_GEN_POL_RISING);
|
||||
/* now enable request generation again */
|
||||
LL_DMAMUX_EnableRequestGen(NULL, LL_DMAMUX_REQ_GEN_0);
|
||||
|
||||
/* we need the EXTI to be configured as interrupt generating line, but no ISR registered */
|
||||
furi_hal_gpio_init_ex(
|
||||
signal->gpio, GpioModeInterruptRiseFall, signal->pull, GpioSpeedVeryHigh, GpioAltFnUnused);
|
||||
|
||||
/* capture current timer */
|
||||
signal->pos = 0;
|
||||
signal->timer_value = TIM2->CNT;
|
||||
signal->gpio_mask = signal->gpio->pin;
|
||||
signal->gpio_value = signal->gpio->port->IDR & signal->gpio_mask;
|
||||
|
||||
/* now set up DMA with these settings */
|
||||
LL_DMA_Init(DMA1, signal->dma_channel, &signal->dma_config_timer);
|
||||
LL_DMA_Init(DMA1, signal->dma_channel + 1, &signal->dma_config_gpio);
|
||||
LL_DMA_EnableChannel(DMA1, signal->dma_channel);
|
||||
LL_DMA_EnableChannel(DMA1, signal->dma_channel + 1);
|
||||
}
|
||||
|
||||
uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us) {
|
||||
uint32_t start_time = DWT->CYCCNT;
|
||||
uint32_t timeout_ticks = timeout_us * (F_TIM2 / 1000000);
|
||||
|
||||
do {
|
||||
/* get the DMA's next write position by reading "remaining length" register */
|
||||
uint32_t dma_pos =
|
||||
signal->size - (uint32_t)LL_DMA_GetDataLength(DMA1, signal->dma_channel);
|
||||
|
||||
/* the DMA has advanced in the ringbuffer */
|
||||
if(dma_pos != signal->pos) {
|
||||
uint32_t delta = signal->timer_buffer[signal->pos] - signal->timer_value;
|
||||
uint32_t last_gpio_value = signal->gpio_value;
|
||||
|
||||
signal->gpio_value = signal->gpio_buffer[signal->pos];
|
||||
|
||||
/* check if the GPIO really toggled. if not, we lost an edge :( */
|
||||
if(((last_gpio_value ^ signal->gpio_value) & signal->gpio_mask) != signal->gpio_mask) {
|
||||
signal->gpio_value ^= signal->gpio_mask;
|
||||
return PULSE_READER_LOST_EDGE;
|
||||
}
|
||||
signal->timer_value = signal->timer_buffer[signal->pos];
|
||||
|
||||
signal->pos++;
|
||||
signal->pos %= signal->size;
|
||||
|
||||
uint32_t delta_unit = 0;
|
||||
|
||||
/* probably larger values, so choose a wider data type */
|
||||
if(signal->unit_divider > 1) {
|
||||
delta_unit =
|
||||
(uint32_t)((uint64_t)delta * (uint64_t)signal->unit_multiplier / signal->unit_divider);
|
||||
} else {
|
||||
delta_unit = delta * signal->unit_multiplier;
|
||||
}
|
||||
|
||||
/* if to be scaled to bit times, save a few instructions. should be faster */
|
||||
if(signal->bit_time > 1) {
|
||||
return (delta_unit + signal->bit_time / 2) / signal->bit_time;
|
||||
}
|
||||
|
||||
return delta_unit;
|
||||
}
|
||||
|
||||
/* check for timeout */
|
||||
uint32_t elapsed = DWT->CYCCNT - start_time;
|
||||
|
||||
if(elapsed > timeout_ticks) {
|
||||
return PULSE_READER_NO_EDGE;
|
||||
}
|
||||
} while(true);
|
||||
}
|
122
lib/pulse_reader/pulse_reader.h
Normal file
122
lib/pulse_reader/pulse_reader.h
Normal file
@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <furi_hal_gpio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define PULSE_READER_NO_EDGE (0xFFFFFFFFUL)
|
||||
#define PULSE_READER_LOST_EDGE (0xFFFFFFFEUL)
|
||||
#define F_TIM2 (64000000UL)
|
||||
|
||||
/**
|
||||
* unit of the edge durations to return
|
||||
*/
|
||||
typedef enum {
|
||||
PulseReaderUnit64MHz,
|
||||
PulseReaderUnitPicosecond,
|
||||
PulseReaderUnitNanosecond,
|
||||
PulseReaderUnitMicrosecond,
|
||||
} PulseReaderUnit;
|
||||
|
||||
/* using an anonymous type */
|
||||
typedef struct PulseReader PulseReader;
|
||||
|
||||
/** Allocate a PulseReader object
|
||||
*
|
||||
* Allocates memory for a ringbuffer and initalizes the object
|
||||
*
|
||||
* @param[in] gpio the GPIO to use. will get configured as input.
|
||||
* @param[in] size number of edges to buffer
|
||||
*/
|
||||
PulseReader* pulse_reader_alloc(const GpioPin* gpio, uint32_t size);
|
||||
|
||||
/** Free a PulseReader object
|
||||
*
|
||||
* Frees all memory of the given object
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
*/
|
||||
void pulse_reader_free(PulseReader* signal);
|
||||
|
||||
/** Start signal capturing
|
||||
*
|
||||
* Initializes DMA1, TIM2 and DMAMUX_REQ_GEN_0 to automatically capture timer values.
|
||||
* Ensure that interrupts are always enabled, as the used EXTI line is handled as one.
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
*/
|
||||
void pulse_reader_start(PulseReader* signal);
|
||||
|
||||
/** Stop signal capturing
|
||||
*
|
||||
* Frees DMA1, TIM2 and DMAMUX_REQ_GEN_0
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
*/
|
||||
void pulse_reader_stop(PulseReader* signal);
|
||||
|
||||
/** Recevie a sample from ringbuffer
|
||||
*
|
||||
* Waits for the specified time until a new edge gets detected.
|
||||
* If not configured otherwise, the pulse duration will be in picosecond resolution.
|
||||
* If a bittime was configured, the return value will contain the properly rounded
|
||||
* number of bit times measured.
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
* @param[in] timeout_us time to wait for a signal [µs]
|
||||
*
|
||||
* @returns the scaled value of the pulse duration
|
||||
*/
|
||||
uint32_t pulse_reader_receive(PulseReader* signal, int timeout_us);
|
||||
|
||||
/** Get available samples
|
||||
*
|
||||
* Get the number of available samples in the ringbuffer
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
*
|
||||
* @returns the number of samples in buffer
|
||||
*/
|
||||
uint32_t pulse_reader_samples(PulseReader* signal);
|
||||
|
||||
/** Set timebase
|
||||
*
|
||||
* Set the timebase to be used when returning pulse duration.
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
* @param[in] unit PulseReaderUnit64MHz or PulseReaderUnitPicosecond
|
||||
*/
|
||||
void pulse_reader_set_timebase(PulseReader* signal, PulseReaderUnit unit);
|
||||
|
||||
/** Set bit time
|
||||
*
|
||||
* Set the number of timebase units per bit.
|
||||
* When set, the pulse_reader_receive() will return an already rounded
|
||||
* bit count value instead of the raw duration.
|
||||
*
|
||||
* Set to 1 to return duration again.
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
* @param[in] bit_time
|
||||
*/
|
||||
void pulse_reader_set_bittime(PulseReader* signal, uint32_t bit_time);
|
||||
|
||||
/** Set GPIO pull direction
|
||||
*
|
||||
* Some GPIOs need pulldown, others don't. By default the
|
||||
* pull direction is GpioPullNo.
|
||||
*
|
||||
* @param[in] signal previously allocated PulseReader object.
|
||||
* @param[in] pull GPIO pull direction
|
||||
*/
|
||||
void pulse_reader_set_pull(PulseReader* signal, GpioPull pull);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user