We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
All,
I am trying to track down a problem of some code that was written by an 'overseas' 3rd party (I will be nice and not state the country of origin).
This code uses a single timer set at a 1mS interrupt rate in order to determine if the SPI is still communicating externally with it's master. If no communications are detected the timer resets the SPI port, clears the interrupt, and jumps to the reset vector. The problem is: the SPI port remains dead until a power cycle is accomplished.
The obvious fix is to use the watchdog (which is what I will eventually do), but I would like to understand the why of why this does not work (yes, bad coding practice is the real reason)...
Since the code jumps to the reset vector this is what I have been able to analyze:
(1) Since this is not a true reset (ie: via watchdog) all hardware registers are not reset - problem potential here. (2) The jump to the reset vector was accomplished while in supervisor mode, so the privleged registers (ie: SP,etc) can be written. (3) The timer interrupt was cleared prior to making the jump to the reset vector, so all interrupts are still enabled. (4) Since this is not a true reset, all resident code can still execute (ie: interrupt handlers). (5) The startup code will reset all initialized data, registers, etc prior to jumping to program main(), effectively returning data to a power up state.
One reason I can currently come up with as to why the SPI is never functional after this occurs is that maybe an interrupt occurs while in the startup code (clearing a tracking variable or resetting the processor registers). But the interrupt would also inhibit the startup code until it was serviced. This potential cause is (probably) not the only reason for this issue, and why I am asking for your input(s).
Unfortunately, this board has no JTAG to connect so stepping through the code is not an option. I could write to the serial port - if it was connected, but it isnt. Right now I am trying to analyze my way through this code before using a 'hammer' approach to solving this problem.
What else am I missing in this analysis? Thanks.
Erik,
Yes, I agree. However (from initial post):
"The obvious fix is to use the watchdog (which is what I will eventually do), but I would like to understand the why of why this does not work (yes, bad coding practice is the real reason)..."
Thanks
Excellent - and now you can use this episode as concrete evidence to show why not having it was such a Bad Idea!
My thoughts too, as my concern is not having a true reset which would reset all chip registers. Looking at the startup.s code does not give me any insight as to why this would be an issue.
It's not startup.s you should be looking at. It's initialization of peripherals, like the interrupt controller and SPI.
Don't waste your time. Get this stuff to work first.
My crystal ball tells me you are using an ARM Cortex-M based processor. Is that correct? The "supervisor" mode was a bit misleading, though.
In a first step you could keep using the timer ISR which supposedly works already. In Cortex-M you can request a proper reset via software. In other ARM based controllers that would be system dependent. Just change your LDR pc, =0 into an appropriate register access.
-- Marcus
I guess this is possible. Found this post relating to the operation of the SPI port:
"Seems it doesn't need the /CS signal unless you really want to use it.
As long as you understand that any glitches on the SCK line during power up will be interpreted as clock pulses and will put the SPI out of synch with no way back without using the /SS line. ie any data can be 1 or more bits out of synch with the clock and will be meaningless."
This SPI is also not using SSEL line.
Interesting...
0001f2 bd30 POP {r4,r5,pc}
I think you may find that this innocuous looking line of code actually performs some processor magic. If I were as curious as you I'd check it out.
Marcus,
Actually, this is not a waste of my time. I like to understand problems (part of my engineering-based constitution, I guess) and then formulate a proper solution.
My crystal ball tells me you are using an ARM Cortex-M based processor
The processor is an ARM7 LPC2103, not stated because of lack of perceived relevance (although in hindsight it probably is relevant - my bad).
In Cortex-M you can request a proper reset via software
Warning: this is not true with all Cortex processors. I was just working with a LPC1765 Cortex M3 and it has NO mechanism to do a software reset.
Thanks.
Jack,
I missed this reply due to the series of posts. Good insight, so I decided to look into this...
I forgot NEVER ASSUME...
This is a link that I have found and am currently reviewing...
www.embedded.com/.../201500001
Thanks for another avenue to follow...
>> My crystal ball tells me you are using an ARM Cortex-M based processor > > The processor is an ARM7 LPC2103, not stated because of lack of > perceived relevance (although in hindsight it probably is relevant - > my bad).
Then how can this be a legal exception return? On ARM7 cores, exceptions must be returned from in ARM state and this is a Thumb instruction.
>> In Cortex-M you can request a proper reset via software > > Warning: this is not true with all Cortex processors. I was just > working with a LPC1765 Cortex M3 and it has NO mechanism to do a > software reset.
All Cortex-M cores support this, but apparently NXP chose (or forgot?) to implement this feature. Well, at least they documented this rather annoying fact.
I mistakenly copied the .asm code from another file I thought was from the right ISR. The actual entry and return is:
000000 e92d5fff PUSH {r0-r12,lr}
return:
000170 e8bd5fff POP {r0-r12,lr} 000174 e25ef004 SUBS pc,lr,#4
which is similar to what is posted on arm website:
0x00001c: LDMFD sp!,{r0-r4,r12,lr} 0x000020: SUBS pc,lr,#4
Thanks for catching this error.
I followed up with NXP be confirm this was not an error - it isnt.
Additionally, ARMs website added the following:
"The IRQHandler C function above must be compiled with armcc, however, C_int_handler() may be a C function compiled for ARM or Thumb. The linker can add any necessary ARM/Thumb interworking veneers to perform the change of state."
If you are nut using slave-select or other hw means to make sure that master and slave are in sync, then you would have to consider bit-stuffing, so that there is unique bit patterns that represents the start of a transmission.
The bad thing with this - when you don't have HDLC hardware - is that you will have to perform a lot of bit shifting in your receive code to both detect the stuffing and to combine data from multiple words and shift and merge into correctly aligned bytes.
It would then probably have been better if an ARM chip with HDLC hw support had been selected.
Per,
Thank you for that interesting suggestion concerning use an HDLC (or equivalent) protocol to ensure proper bit synchronization. IF this device ever gets to the point of an upgrade this suggestion will be at the top portion of the list. I also did a Layer 2 protocol for a SS freq hop system in the ISM band that used a preamble/delimiter type data frame that could also work.
In addition to the awful (being nice again) design of this device to begin with, the SPI has known issues concerning noise sensitivity (surprise, surprise - NOT). In so far as correcting this synchronization issue I have been toying around with an algorithm on the master side that may accomplish a re-synchronization of the master/slave devices in the following manner (again, this is just in the Hmmm stage):
If the slave device does not respond or does not properly respond to a command, do the following:
Switch the master SCK line to GPIO and send 7 clock pulses to the slave. Switch the master SCK back to SPI and then resend the last command. If the response is still corrupted perform the sequence above again except send 6 clocks. Continue in this manner (done to a single clock) until a correct response is received, then return to normal SPI operation.
Sounds like it would work, but switching the SCK from SCK to GPIO may induce unwanted glitches on the line which would nullify any chance that this would work at all.
Let's say that your SPI data may never contain 10 bytes in a row with value 0x00.
So if the master doesn't get an answer - send 12 or more zeroes. Then make a pause of say 50 ms. Then start sending real data again.
The slave should be able to notice the long row of zeroes and know that it is a request to synchronize. When it then sees the pause, it can reinitialize the SPI controller and start waiting for more data, having cleared the internal SPI bit counter.
This method wastes a bit of time for synchronizing, but have the advantage that when the sender and receiver are synchronized, you will be able to keep a quite high speed without wasting time performing a lot of bit manipulations in the slave. Counting # of consecutive 0x00 is quite cheap. And only after having seen at least 10 bytes of zero do you need to start measuring if you have a pause in the transfer (which is needed since you may get 10, 11 or 12 zero bytes).
The next thing to do if the interface suffers from noise is of course to make sure that all messages have strong integrity checking. At least crc-32 but possibly even better. Maybe you should even consider a twodimensional scheme.
An even better, less time-consuming recovery method!
Excellent suggestion. Thanks.