Wait for Pin State or Count - attempts to make fastest ASM code
Timothy D. Swieter
Posts: 1,613
In a couple of my projects recently, I've come across situations where I need to wait for a pin change while also watching a counter for timeouts.
In the first case, it is for protocol processing. The pin is an input and wiggling with data coming in. Part of receiving the protocol is to allow for some variability in receiving, but if nothing is received in a certain amount of time the packet should be processed and then go back to waiting for the start signature of the next packet. So, I'm watching a pin changing, but also want to keep an eye on a counter. Note this isn't checking a counter every time a pin changes, because the pin could be held low (or high) for a long duration, causing a timeout.
In the second case, every so often (determined by the counter), a set of processing is occurring. But, if a pin changes to a specified state (at any time) then a specific processing should take place. Note this isn't if the pin is at the specified state when the timer exceeds, but that the special processing should occur 'immediately' when the pin changes state.
So, below is my first pass at an ASM Snippet that may work for the above situations. It gets the current state of the pins and the counter. Checks both of them, and if either condition exists it goes to the proper section of code. The priority of either condition is determined by which is checked first. The code doesn't include a check yet for CurrentCnt or TargetCnt rollover. I've not tried running this yet, because of lack of equipment while I am traveling. I thought it would be good to bounce it off the community and see if this could be made tighter (faster) or if someone has already tackled this problem. Another avenue I may explore is employing Counter A or Counter B, if it helps to get this as fast as possible.
This is just a snippet, assume I/O and Targets are set up prior to entering this stage of the code.
In the first case, it is for protocol processing. The pin is an input and wiggling with data coming in. Part of receiving the protocol is to allow for some variability in receiving, but if nothing is received in a certain amount of time the packet should be processed and then go back to waiting for the start signature of the next packet. So, I'm watching a pin changing, but also want to keep an eye on a counter. Note this isn't checking a counter every time a pin changes, because the pin could be held low (or high) for a long duration, causing a timeout.
In the second case, every so often (determined by the counter), a set of processing is occurring. But, if a pin changes to a specified state (at any time) then a specific processing should take place. Note this isn't if the pin is at the specified state when the timer exceeds, but that the special processing should occur 'immediately' when the pin changes state.
So, below is my first pass at an ASM Snippet that may work for the above situations. It gets the current state of the pins and the counter. Checks both of them, and if either condition exists it goes to the proper section of code. The priority of either condition is determined by which is checked first. The code doesn't include a check yet for CurrentCnt or TargetCnt rollover. I've not tried running this yet, because of lack of equipment while I am traveling. I thought it would be good to bounce it off the community and see if this could be made tighter (faster) or if someone has already tackled this problem. Another avenue I may explore is employing Counter A or Counter B, if it helps to get this as fast as possible.
This is just a snippet, assume I/O and Targets are set up prior to entering this stage of the code.
'Get the current state of the inputs and counter :Loop mov CurrentPins, ina mov CurrrentCnt, cnt 'Check for time to be exceeded cmp CurrrentCnt, TargetCnt wz, wc if_nz_and_nc jmp #:TimeExceeded 'Use only one of the following low or high checks 'Use if checking for a pin to be low and CurrentPins, TargetPinMask wz if_z jmp #:PinChanged 'Use if checking for a pin to be high ' and CurrentPins, TargetPinMask wc ' if_c jmp #:PinChange jmp #:Loop :PinChanged nop :TimeExceeded nop
Comments
I've only skimmed your post but wouldn't this bit of code work? (in principal)
Yes, I think that will work, and is more concise than what I posted. Boy I just love getting inspired by the community.
I'm thinking through your code and it is making sense to me. A timeout (loop count) is calculated based on the known number of clock cycles and frequency of the Prop. You keep looping for the define period (could be defined before runtime or at runtime with a couple calcs). When the new lot bit is detected, the code falls through to the start bit. I'm curious as to why having a "if_c" on the front of the timeout jump and not just have a "if_nz"? Perhaps when testing for one bit, this is the same.
Just to be sure, with the Assembly Conditions, the instruction only takes 4 clock cycles if the condition doesn't allow execution otherwise it takes the appropriate number of clock cycles for the instruction, true? I hadn't thought much about that until now but it must take some time.
The C bit is set if there are an odd number of 1's following the test instruction. So this is useful to determine whether the bit was high or low as the Z bit will always be zero following a timeout so nz distinguishes a timeout and c/nc the signal. The granularity of the test/djnz combo results in 100ns latency which is fine for almost all but the very high speed signals. If you want a 5ms timeout then load (via a long) 50,000 into the timeout. Fortunately DJNZ is optimized for loops taking only 4 cycles normally but 8 when it exits so if you want to be absolutely precise that's another 50ns for a timeout