|
|
View previous topic :: View next topic |
Author |
Message |
iso9001
Joined: 02 Dec 2003 Posts: 262
|
Need help writing to custom bus protocol |
Posted: Thu May 11, 2006 2:35 pm |
|
|
Hi,
I'm working on a project that requires me to read in and write on a 1 wire serial communiction. Its a stupid old system that was proprietary and no one has done anything like it I beleive. Its very similar to OBD2 VPW but not exact, uses different timings, voltage, data length, crc etc
The problem I have is my board has to be taking adv readings and toggling/checking digital pins on a timed basis (my shortest event is every 2ms). So while I'm doing all this other stuff I'm finding it difficult to find the time to read in this bus and when I go to write out to it, I block the chip from doing anything till I'm done bit banging the signal out.
For example, my write code (which is WAY more important then reading, but I'de like to do both):
// S = 200us
// L = 400us
//Start writing
output_high(BUS); delay_us(L); //1
output_low(BUS); delay_us(L); //0
output_high(BUS); delay_us(S); //0
output_low(BUS); delay_us(L); //0
output_high(BUS); delay_us(S); //0
output_low(BUS); delay_us(S); //1
output_high(BUS); delay_us(S); //0
output_low(BUS); delay_us(S); //1
... etc etc
output_high(BUS); //done
//go back to doing stuff now,
I know it doesn't seem like much, but those uSeconds add up. I think this should be possible without using delays.
I'm trying to figure out a way to not block the chip while I'm writing. My longest side task takes me 30us. So I spend a lot of time in the main loop not doing anything, I figure maybe I could set a flag like WRITE_DATA=TRUE, and then have a few bytes of data stored somewhere I could passively bit bang out.
I know this is possible but I've had a hard time figuring out what I need to do,
Anyone have any suggestions ?? A passive read() part is next,
Thanks!
(using pic 16F87X but could switch if needed) |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Thu May 11, 2006 3:08 pm |
|
|
I had a similar issue. I have two processors - one is concerned with the user interface, and the other is locked away in a vault. They communicate via CAN bus.
The user interface processor has an IR decoder to allow 'pass through' of IR commands from a standard remote control. The user can use the remote to be able to control some A/V equipment locked away in the vault. The user interface processor receives/decodes/compresses the IR bitstream and sends it to the remote processor via the CAN bus. Once all information necessary to reconstruct the IR bitstream has been received by the remote processor, it then starts to "beam" that information to the A/V equipment.
This is where our situations are similar. I couldn't use an approach that involved delays because this processor also communicates with many other processors, and requests for information can come at any time and must be serviced immediately (more or less). When those requests come, they can't interfere with the IR transmission either because that would result in a miscommunication with the A/V equipment (and that can't be tolerated).
What I did was set up a rather complicated but very robust algorithm involving a timer interrupt. The general idea is that you load the data that must be transmitted, set the output pin appropriately, and set the timer to go off at the appropriate time (which for you appears to be either 200 us or 400 us). You also load the next timer value at this point in time as well to speed things along the next time the interrupt occurs. When the timer interrupts, you toggle the state of the output pin, reload the timer with the value you 'set aside' earlier, and load the timer value for next time. This goes on until you're done sending the information.
There is a rather small latency between when the interrupt fires and the processor actually gets around to servicing the interrupt, but this time should be more or less the same no matter what the processor is doing. The net effect is that the timing accuracy on your output waveform is relatively good. There will be some jitter, but that will be relatively small.
I can't really elaborate more as this is proprietary stuff, but you should get the idea. |
|
|
iso9001
Joined: 02 Dec 2003 Posts: 262
|
|
Posted: Thu May 11, 2006 4:09 pm |
|
|
Hey newguy,
Yea I know what your talking about, thats kind of what I had planned.
If I'm in a 20us delay for my adc and the timer goes off it will still jump to the timer_isr right ? I don't know if I've ever had to check what happens to an interupt request when the pic is in a loop, pretty sure it would jump...
Hmmmmmm..... I could set that up.
Are you setting a flag in the interupt that the main loop checks later and changes the data or are you doing all the work (shifting next data bit, reloading timer, changing output state) there inside the isr? I could do the later although I think its against recommended practice, and I'd have to make my data global.
I'll have to think about this for a bit, I'll certainly try it tho.
One thing is I can see it handeling the writes, reads may be another issue tho. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Thu May 11, 2006 4:38 pm |
|
|
iso9001 wrote: | Hey newguy,
Yea I know what your talking about, thats kind of what I had planned.
If I'm in a 20us delay for my adc and the timer goes off it will still jump to the timer_isr right ? I don't know if I've ever had to check what happens to an interupt request when the pic is in a loop, pretty sure it would jump...
Hmmmmmm..... I could set that up.
Are you setting a flag in the interupt that the main loop checks later and changes the data or are you doing all the work (shifting next data bit, reloading timer, changing output state) there inside the isr? I could do the later although I think its against recommended practice, and I'd have to make my data global.
I'll have to think about this for a bit, I'll certainly try it tho.
One thing is I can see it handeling the writes, reads may be another issue tho. |
I'm not sure about an interrupt being able to break out of a delay loop. Depends how the compiler handles it. My gut says that it should happen this way, but I wouldn't count on it.
The timer interrupt itself just takes whatever the next delay value is (which is set in the main routine) and adds that to the current value of the timer. Check the code library for a little writeup called "timers - what you should know" that I posted as to why I do it in this manner if it doesn't make sense. The interrupt then sets a flag that the main routine uses to load the next value that the interrupt will need the next time it fires. I think that the load routine also sets a "all done" flag that deactivates everything if no more data is waiting to be clocked out. That's about it. It sounds like the timer interrupt is doing a lot, but it really isn't. All it does is toggle the output line, reads the current value of the timer, adds the next delay value to it, and reloads the timer with this new value. It then sets a flag, and it's done. Not very many instructions at all, so the timer interrupt is quick.
I had to break up data retrieval (i.e. the next timer value) and setting the timer like this because an IR data stream isn't a simple thing to reconstruct. Fetching the next timer value isn't a simple matter because it can vary a lot (i.e. it's not fixed as x for a 1 and y for a 0). It sounds like your app is simple enough to be able to do everything inside the timer isr.
The best way to handle the reads would be using an external interrupt pin or the interrupt-on-change, if the way things are wired allows it, or if you can rewire it in this manner. Then just coordinate changes in pin state with a free running timer and figure out your 1's and 0's from that information. If not, then you pretty much have to poll the input pin in main.
Polling becomes more of a pain, but you can still use a timer to try & figure out the bit times - it's just that the values you read will have more jitter which will make deciding what is what a bit more difficult. My polling algorithm idea is this:
in main() have this:
Code: | if (input(COMM_PIN) != last value) {
last_value = input(COMM_PIN);
start_inactivity_timer();
read_and_save_timer_value();
} |
You'd also need another timer interrupt (I called it the "inactivity timer"). The basic idea being that while data is coming, the inactivity timer acts like the watchdog - every time you get a change/bit, "pet" this watchdog. If a long period with no activity on the comm line goes by, this timer expires and then you can examine the timer values you saved when data was coming in. From those values, you should be able to determine 1's and 0's. |
|
|
Mark
Joined: 07 Sep 2003 Posts: 2838 Location: Atlanta, GA
|
|
Posted: Thu May 11, 2006 5:45 pm |
|
|
Use the CCP for your transmission. Create a state machine and load a CCP value that represents your delay time. Somewhere in your code you would start the transmission. The would reset the state machine and enable the CCP interrupt. Now your program can resume operation and the transmission will be handled by the interrupt. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|