views:

149

answers:

3

I'm trying to send data to an SD card from a PIC18f4580, but the PIC is not sending what it should be.

related global variables:

unsigned char TXBuffer[128]; //tx buffer
unsigned char TXCurrentPos = 0x00; //tracks the next byte to be sent
unsigned char TXEndPos = 0x00; //tracks where new data should be put into the array

I am adding data to a buffer using the following function:

void addToBuffer(char data){

    TXBuffer[TXEndPos] = data;
    TXEndPos++;
}

And putting the data from the TXBuffer into TXREG with the following interrupt:

else if (PIR1bits.TXIF == 1){

    if((TXEndPos - TXCurrentPos) > 0){         // if there is data in the buffer
        TXREG = TXBuffer[TXCurrentPos];           // send next byte
        TXCurrentPos++;               // update to the new position
    }

Using an oscilloscope I am able to see that the PIC is sending 0x98, regardless of what I put into the buffer. In fact I never put 0x98 into the buffer.

However, if I replace

TXREG = TXBuffer[TXCurrentPos];

with

TXREG = 0x55;

or

TXREG = TXCurrentPos;

then I get the expected results, that is the PIC will send 0x55 repeatedly, or count up from 0 respectively.

So why does the PIC have trouble sending data from the array, but any other time it is fine? I'll emphasize that transferring is handled in an interrupt, because I feel like that's the root of my issue.

EDIT: It is a circular buffer in the sense that TXEndPos and TXCurrentPos return to 0 when they reach 127. I also disable the transmit interrupt when TXEndPos - TXCurrentPos == 0, and re-enable it when adding data to the buffer. Really, my code works completely as expected in that if I add 13 characters to TXBuffer in main, my PIC will transmit 13 characters and then stop. The problem is that they are always the same (wrong) character - 0x98.

EDIT2: more complete functions are here: http://pastebin.com/MyYz1Qzq

+1  A: 

There are a lot of things you are not taking care of. TXBuffer needs to be a circular buffer. Once you increment TXEndPos past 127 then you need to wrap it back to 0. Same for TXCurrrentPos. That also affects the test to see if there's something in the buffer, the > 0 test isn't good enough. Generic advice is available here.

Hans Passant
It doesn't *have* to be a circular buffer. It depends on what the OP wants to do. Reading the code it seems he wants to setup for a transmission of a single message that is <128 bytes.
Guy
+1  A: 

Your code is incomplete, but it looks wrong as-is: what happens if there is nothing to send? You don't seem to load TXREG then, so why would anything be transmitted, be it 0x98 or anything else?

The way it is usually done when this kind of code architecture is used is to turn off TXIE if there is nothing to send (in a else part of the IRQ routine), and turn it on unconditionally at the end of the addToBuffer function (since you then know for sure that there is at least one character to send).

Also, you should test TXEndPos and TXCurrentPos for equality directly, since that would let you use a circular buffer very easily by adding two modulo operations.

Samuel Tardieu
I have all of those concerns taken care of. I edited my post to address those. I was just trying to make my question as short as possible and cut out a lot of my controls for stuff that I didn't think applied to my problem. :P
John Moffitt
+2  A: 

Perhaps TXBuffer doesn't really contain the data you think it does? Maybe you're not calling addToBuffer or calling it at the wrong time or with the wrong parameter?

You can try something like this in your interrupt handler:

TXBuffer[TXCurrentPos] = TXCurrentPos;
TXREG = TXBuffer[TXCurrentPos];
TXCurrentPos++;

Just to prove to yourself you can read and write to TXBuffer and send that to the USART.

Also try:

TXREG = TXEndPos;

To see if this matches your expectation (= the length of your message).

I am assuming there's some other code we're not seeing here that takes care of starting the transmission. Also assuming this is done per message with the position being reset between messages - i.e. this is not supposed to be a circular buffer.

EDIT: Based on looking at the more recently posted code: Don't you need to kickstart the transmitter by writign the first byte of your buffer to TXREG? What I would normally do is enable the interrupt and write the first byte into the transmit register and a quick look at the datasheet seems to indicate that's what you need to do. Another thing is I still don't see how you ensure a wraparound from 127 to 0?

Also your main() seems to just end abruptly, where does the execution continue once main ends?

Guy
I have already tried TXREG = TXEndPos and got the expected result, the length of my message. Trying to write to the TXBuffer and then read from the TXBuffer did not work; I got the same character as I've been getting.
John Moffitt
Perhaps TXBuffer isn't mapped to the right memory location. Are you using the MPLAB C compiler? Try putting "#pragma udata TXBuffer" before the declaration of TXBuffer. Also review all the settings to see that they match your PIC. I think there should be a linker script specific to this PIC. Another thing to try is running in the simulator.
Guy