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:
g3gg0.de 2023-05-09 02:55:17 +02:00 committed by GitHub
parent f57f0efc48
commit e1c6e78b2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1024 additions and 88 deletions

View File

@ -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

View File

@ -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

View File

@ -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 entry status name type params
2 Version + 26.1 26.2
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
156 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h
157 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h
158 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h
159 Header + lib/pulse_reader/pulse_reader.h
160 Header + lib/toolbox/args.h
161 Header + lib/toolbox/crc32_calc.h
162 Header + lib/toolbox/dir_walk.h
1576 Function + protocol_dict_render_data void ProtocolDict*, FuriString*, size_t
1577 Function + protocol_dict_set_data void ProtocolDict*, size_t, const uint8_t*, size_t
1578 Function - pselect int int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*
1579 Function - pulse_reader_alloc PulseReader* const GpioPin*, uint32_t
1580 Function - pulse_reader_free void PulseReader*
1581 Function - pulse_reader_receive uint32_t PulseReader*, int
1582 Function - pulse_reader_samples uint32_t PulseReader*
1583 Function - pulse_reader_set_bittime void PulseReader*, uint32_t
1584 Function - pulse_reader_set_pull void PulseReader*, GpioPull
1585 Function - pulse_reader_set_timebase void PulseReader*, PulseReaderUnit
1586 Function - pulse_reader_start void PulseReader*
1587 Function - pulse_reader_stop void PulseReader*
1588 Function - putc int int, FILE*
1589 Function - putc_unlocked int int, FILE*
1590 Function - putchar int int

View File

@ -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

1 entry status name type params
2 Version + 26.1 26.2
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/cli/cli.h
5 Header + applications/services/cli/cli_vcp.h
175 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_usart.h
176 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_utils.h
177 Header + lib/stm32wb_hal/Inc/stm32wbxx_ll_wwdg.h
178 Header + lib/pulse_reader/pulse_reader.h
179 Header + lib/subghz/blocks/const.h
180 Header + lib/subghz/blocks/decoder.h
181 Header + lib/subghz/blocks/encoder.h
739 Function + dialog_message_show DialogMessageButton DialogsApp*, const DialogMessage*
740 Function + dialog_message_show_storage_error void DialogsApp*, const char*
741 Function - difftime double time_t, time_t
742 Function - digital_sequence_add void DigitalSequence*, uint8_t
743 Function - digital_sequence_alloc DigitalSequence* uint32_t, const GpioPin*
744 Function - digital_sequence_clear void DigitalSequence*
745 Function - digital_sequence_free void DigitalSequence*
746 Function - digital_sequence_send _Bool DigitalSequence*
747 Function - digital_sequence_set_sendtime void DigitalSequence*, uint32_t
748 Function - digital_sequence_set_signal void DigitalSequence*, uint8_t, DigitalSignal*
749 Function - digital_sequence_timebase_correction void DigitalSequence*, float
750 Function - digital_signal_add void DigitalSignal*, uint32_t
751 Function - digital_signal_add_pulse void DigitalSignal*, uint32_t, _Bool
752 Function - digital_signal_alloc DigitalSignal* uint32_t
753 Function - digital_signal_append _Bool DigitalSignal*, DigitalSignal*
754 Function - digital_signal_free void DigitalSignal*
2179 Function + protocol_dict_render_data void ProtocolDict*, FuriString*, size_t
2180 Function + protocol_dict_set_data void ProtocolDict*, size_t, const uint8_t*, size_t
2181 Function - pselect int int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*
2182 Function - pulse_reader_alloc PulseReader* const GpioPin*, uint32_t
2183 Function - pulse_reader_free void PulseReader*
2184 Function - pulse_reader_receive uint32_t PulseReader*, int
2185 Function - pulse_reader_samples uint32_t PulseReader*
2186 Function - pulse_reader_set_bittime void PulseReader*, uint32_t
2187 Function - pulse_reader_set_pull void PulseReader*, GpioPull
2188 Function - pulse_reader_set_timebase void PulseReader*, PulseReaderUnit
2189 Function - pulse_reader_start void PulseReader*
2190 Function - pulse_reader_stop void PulseReader*
2191 Function - putc int int, FILE*
2192 Function - putc_unlocked int int, FILE*
2193 Function - putchar int int

View File

@ -28,6 +28,7 @@
"flipperformat",
"toolbox",
"nfc",
"pulse_reader",
"microtar",
"usb_stm32",
"st25rfal002",

View File

@ -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",

View File

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

View File

@ -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

View 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")

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

View 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