_|_|_|_|_| _| _| _|_|_|_| _|_| _| _| _|_|_|_| _| _| _| _| _| _| _|_| _| _| _| _|_|_|_| _|_|_| _| _| _| _| _| _|_|_| _| _| _| _| _| _| _| _|_| _| _| _| _| _|_|_|_| _|_| _| _| _|_|_|_| - aka D1
Description
D1 detects short asynchronous transmissions, like commands from remote controls, using IR or RF. The command is stored in ram and compared with EEPROM. If a match is found, the id of the command is returned. D1 can easily learn new commands by changing mode to save received commands in EEPROM instead of matching them.
D1 also works great with RFID and magnetic card readers, temperature loggers, accelerometers and other serial devices if a built in hardware USART is not available in the selected microcontroller.
Features
- Easy to use
- Compact size
- No hardware USART needed
- Identifies almost any asynchronous transmission
- Perfect for remote control applications, matching IR or RF commands
- Highly configurable
- Debug signaling included for easy debugging
Image showing how an incoming IR remote control signal (ch1) is sampled (ch2)
Development
Receiving and decoding remote control codes are quite tricky. There are numerous different standards and debugging the code takes a lot of time, even with good tools like DSO or logic analyzers. You can find some code for different standards like RC5, but I could not find a decent generic version. A couple years ago, I decided to write my own generic version, it grew to a huge project spanning over several years, but eventually it worked great, and I have used it in numerous projects since.
D1 can be implemented in almost any microcontroller since it does not use any hardware USART. All it needs is a port interrupt to detect incoming transmissions and an 8-bit timer (tmr0) to generate the sampling clock. The routine can be configured in several ways like ignoring the first bits or abort sampling if transmission is interrupted. The clock can be sampled in three different ways depending on the application. The routine is written to be both generic and compact; this is of course a contradiction. If only a specific mode is needed the other modes can easily be deleted to save some space. Some applications need a more dedicated routine; D1 can then be the base that is slightly modified to work with the application. For example I have used it in a project where it was modified to only react on a certain start condition, in this way the risk for triggering on noise is reduced and the routine will more accurately detect all commands.
D1 is written in assembly (for highest performance and smallest size) and can easily be integrated in high level languages, like C using in-line assembler. It consists of two files:
- D1.inc – Samples the data and stores it in ram
- D2.inc – Handles received data, works in two modes:
- COMPARE – Compare D1 data with EEPROM (identify/decode commands)
- SAVE – Stores received data in EEPROM (learning a new command)
D1 uses interrupt (timer & port int) to sample the data. It is very easy to use, initialize the routine and it will sample incoming transmissions. When a transmission has been received in ram, the main program is informed through the D1_RECIEVED flag (set). D1 uses the interrupt pin to sample data. Port on change int can be used instead, but it is less accurate, due to that pic microcontrollers will sometimes miss this event (if a goto instruction is being handled during the interrupt).
Note: Make sure the main program does not change any D1 registers including tmr0 and the prescaler. Disable GIE or INTE/ T0IE in main program when a command is processed to make sure D1 does not run in background.
EXAMPLE:
D1_INIT_MATCH ; Initialize D1 for reception (auto) main_loop: D1_TSR ; D1 Test Skip If Received goto main_loop ; Wait for data... bcf INTCON,GIE ; Disable global interrupt D2_COMPARE ; Identify transmission incfsz D2_CODE,w goto main_match goto main_end ; Unknown transmission main_match: ; Transmission identified .... ; Transmission id in D2_CODE main_end: D1_CLEAR ; Clear received flag bsf INTCON,GIE ; Enable global interrupt goto main_loop ; Wait for new data...
-----------------------------------------------------------------
INTERNAL CLOCK MODES
The tricky part with asynchronous transmission is the lack of a common clock signal. D1 can generate the internal clock using three different modes:
- AUTOMATIC – the clock is automatically adjusted to the shortest pulse in the transmission.
+ The advantage with this method is that it is easy to use and works with most transmissions.
– The disadvantage is that it can’t detect several identical bits in the beginning of a transmission. It will only detect a long “1” instead of “111”. This means that all transmissions will not be stored correctly. To compare remote control commands this is most likely not a problem, but if you would like to analyze or save transmissions, the saved data might differ from the real transmission.
Use D1_INIT_MATCH or D1_INIT_SAVE for this mode
- FIXED – the clock is locked to a fix value.
+ The advantage is that it will work correct with any transmission sending close to the given frequency.
– The disadvantage is that it will not work with different transmissions at different speeds, for example several different remote controls.
Use D1_INIT_MATCH_STATIC (pul_min) or D1_INIT_SAVE_STATIC
- TRIG – Uses two identical transmissions, the first is used to detect the frequency and the second is used to get the data.
+ The advantage is that it will record correct transmissions even if identical bits are transmitted in the beginning of the transmission. It will also adapt to different transmission speeds, allowing detecting transmissions from different remote controls for instance.
– The disadvantage is that it requires that each transmission has to be transmitted repeatedly. The detection of a transmission is slower, since it has to be transmitted two times.
Use D1_INIT_MATCH_TRIG or D1_INIT_SAVE_TRIG for this mode
———————————————————————-
VERIFY
The received transmissions can be verified before setting D1_RECIEVED flag. This is done using the SAVE macros (verify mode) while the MATCH macros does not verify the transmission. Verify only works if each transmission is sent repeatedly (like most remote control transmissions). With verify enabled a successful reception will only be reported if two following, identical transmissions are recorded. Always use this mode when you are saving data. In most cases there is no need to use it to decode commands since there is a low probability that a bit error will result in matching a different command.
———————————————————————-
TIMING AND SAMPLES
D1 uses interrupt (timer & port change) to sample the data in the following way.
__ _ ___ _ |_| |_| |_| ^ ^ ^ ^ ^ ^ <- d1_gpio_int - port change interrupt ^ ^ ^ ^ ^ ^ <- d1_tmr0_int - timer interrupt (sample) |-| <- IR_PUL_MIN - sample interval
- d1_gpio_int – adjusts the tmr0 timer at each port change interrupt (tmr0=(0xff-IR_PUL_MIN)/2). d1_gpio_int finds the shortest pulse and sets the sampling interval (IR_PUL_MIN) to that one
- d1_tmr0_int – sample and store each bit at tmr0 interrupt. It also finds the end of transmission
- IR_PUL_MIN – the sample interval (the shortest period of time between two d1_gpio_int). Can be generated in three ways (see INTERNAL CLOCK MODES above)
———————————————————————-
SKIP FIRST BITS
This is useful to save memory. Skipping the first bits might also solve the problem with two different remote control commands from the same button. (used to detect if key is held, or pressed repeatedly)
___ _ _ _ |_| |_| |_| | ^ ^ ^ <- d1_tmr0_int using D1_SKIP_BITS = 3 0 1 2 3 4 5...
Define D1_SKIP_BITS in main program, like this: #define D1_SKIP_BITS .5 ; Nr of initial bits to skip
———————————————————————-
BITSTOP
Used to avoid sampling wrong bits due to clock mismatch. After X identical bits D1 stops saving data. Besides avoiding sample errors this also saves memory.
_ _ ______________ _ |_| |_| |_| ^ ^ ^ ^ ^ ^ .. ^ ^ - - ^ "^" - sample 0 0 0 1 2 .. 7 8 - - 0 "-" - no sample
Define BITSTOP in LOWER nibble of BITSTOP, like this: #define D1_BIT_STOP 0x18 ; Init state for bit counter If lower nibble is < 8, last bits of transmission won't be saved. UPPER nibble of BITSTOP is used for EoT count (see beneath).
———————————————————————-
END OF TRANSMISSION (EoT)
When the transmission is defined over
_ _ ____________________ |_| |_| ^ ^ ^ ^ ^ ^ ^ .. ^ ^ 1 2 3 .. 9 eot*
* end of transmission (EoT) after xxx identical bits
Define eot_count in upper nibble of BITSTOP. eot_count = BITSTOP + upper nibble. Upper nibble has to be >= 1. example: (eot after 9 identical bits, bitstop after 8 ) #define D1_BIT_STOP 0x18 ; Init state for bit counter
———————————————————————-
SAMPLE SPEED
The sample interval is controlled by the prescaler to tmr0. To get good measurements it is important that a proper sample interval is chosen to match the sampled data. 32 tmr0 counts is a good value for the shortest pulse. For most IR remote controls this is reached with a 1:16 prescaler @ 4MHz
Shortest sample Longest sample PS2,1,0 tmr0 rate tmr0=0xff tmr0=0x00 000 1:2 2e-6[s] (@4MHz) 0.512e-3[s] 001 1:4 4e-6[s] 1.024e-3[s] 010 1:8 8e-6[s] 2.048e-3[s] 011 1:16 16e-6[s] 4.096e-3[s] 100 1:32 32e-6[s] 8.192e-3[s] 101 1:64 64e-6[s] 0.016384[s] 110 1:128 0.128e-3[s] 0.032768[s] 111 1:256 0.256e-3[s] 0.065536[s]
example: (Set prescaler to 1:16, bit 7-4:0 3:PSA 2:PS2 1:PS1 0:PS0) #define D1_PRESCALE 0x03 ; Set prescaler to 1:16
———————————————————————-
D1_SIZE
D1 only saves D1_SIZE bytes in RAM. If transmission is longer, the extra bits are ignored. If transmission is shorter, the remaining bits in RAM are cleared.
example: (save three bytes in RAM + D1_PUL_MIN) #define D1_SIZE .4 ; SIZE in bytes saved in RAM
———————————————————————-
DEBUG MODE
There are several lines marked with ***DEBUG***. These lines can be deleted to save memory, but they are very useful if you have problems and need debugging. Define D1_DEBUG port in main program and connect it to a sample oscilloscope/ logic analyzer
———————————————————————-
OPTIMIZING
D1 is written to be both generic and compact. This is of course a contradiction. You can make D1 smaller by deleting modes that you are not using. For instance, if you are not using verify mode, you can make the routine even smaller.
==================================================================
D1 mode/flag matrix
D1_VERIFY | D1_VERIFY_TMP | | D1_USE_TRIG | | | D1_GET_MIN | | | | D1_NO_SAVE MODES NUMBER TYPE v v v v v Done? ----------------------------------------------------------------- D1_MATCH 1 SAVE 0 0 0 0 0 > d1_end_done ----------------------------------------------------------------- D1_MATCH_STATIC 1 SAVE 0 0 0 1 0 > d1_end_done ----------------------------------------------------------------- D1_MATCH_TRIG 1 TRIG 0 0 1 0 1 > d1_end_more 2 SAVE 0 0 1 1 0 > d1_end_done ----------------------------------------------------------------- D1_SAVE 1 SAVE 1 0 0 0 0 > d1_end_more 2 VERIFY 1 1 0 0 0 > d1_end_done ----------------------------------------------------------------- D1_SAVE_STATIC 1 SAVE 1 0 0 1 0 > d1_end_more 2 VERIFY 1 1 0 1 0 > d1_end_done ----------------------------------------------------------------- D1_SAVE_TRIG 1 TRIG 1 0 1 0 1 > d1_end_more 2 SAVE 1 0 1 1 0 > d1_end_more 3 VERIFY 1 1 1 1 0 > d1_end_done ------------------------------------------------------------------
Documents
D1 source code (PIC 12F629 assembler)
Licensing
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
3 Replies to “Generic routine for receiving short asynchronous transmissions”
Comments are closed.