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;

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];


TXREG = 0x55;


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

EDIT3: By changing our linker script and appropriately using pragmas we're able to get some of our data into our buffer. We statically placed variables related to the buffer into the same memory bank, and that has certainly helped but not all of our data is getting into our buffer. Strangest of all, running the program multiple times yields different data in the buffer, even though all of the data to be placed in the buffer is defined in our code. Also, we get drastically different results when the pic automatically runs after programming it, when we cut the power and restart power with the pickit3 still plugged in, and when we reset the power with the pickit3 disconnected. Also, simulating in MPLAB results in the buffer being filled perfectly, however when actually running the code on the PIC and viewing it's memory, it is not.

Our code as of 5:38 9/22/10 is here.

Our modified linker file is here.

  • \$\begingroup\$ What compiler are you using. It looks similar to the c18, which I work with, let me know and I may just compiler this for a PIC and test it myself. \$\endgroup\$ – Kortuk Sep 19 '10 at 23:30
  • \$\begingroup\$ @Kortuk: I'm using MPLAB c18 Lite. If you would like to give it a whirl it's here: pastebin.com/ZYcZG0eW . \$\endgroup\$ – John Moffitt Sep 20 '10 at 0:38
  • \$\begingroup\$ John, My next note when I loaded it up was going to be the use of volatile. If you are using something that will be modified both inside interrupts and outside, mark it volatile. This however should not be an issue as the c18 lite does not optimize, and without an optimizer the volatile means nothing. \$\endgroup\$ – Kortuk Sep 20 '10 at 1:06
  • \$\begingroup\$ It will be later in the week before I am in a lab with free time. My students have work due on thursday, I will try to load it up by then, it may be next week. \$\endgroup\$ – Kortuk Sep 20 '10 at 1:08
  • \$\begingroup\$ We changed all variabled modified or read inside of the interrupts to volatile but it did not change anything as expected. If we resolve our issue before then I will post back here to let you know. I may end up rewriting a large portion of the code to see if that helps. What's in main right now is just initialization followed by test code at the moment, right now just adding some data to an array and then turning on the TX interrupt. Thanks for taking a look! \$\endgroup\$ – John Moffitt Sep 20 '10 at 1:13

It's a little confusing that the code in the question differs to the code in pastebin - but assuming that the pastebin code is correct, I'd say that the problem is that you're sending uninitialised data from the array instead of the characters that you put in it.

TXREG = TXBuffer[TXEndPos];           // send next byte

should be

TXREG = TXBuffer[TXCurrentPos];           // send next byte

It's important to copy/paste the code directly (using pastebin is great!) because you've actually corrected this yourself in the question - making it hard for people to spot the problem :)

This may have been easier to spot yourself if you initialised the buffer to a known value on startup (eg 0x5A).

You also mention that you reset the TxCurrentPos & TxEndPos variables to zero at some point, but I can't see that in your code. It's best to do this whenever you increment those values. Eg

TxCurrentPos %= TX_BUFFER_SIZE;

Aside: I like that you've removed the redundant TxBufferSize variable in the question - this follows the DRY Principle and eliminates the source for an error. Having data duplicated in two spots is asking for trouble.

  • \$\begingroup\$ I was going to initialize the code and then run it to see if there was a change, and if there was, this is what type of error I was going to look for. Good catch \$\endgroup\$ – Kortuk Sep 20 '10 at 17:02
  • \$\begingroup\$ That mistake was a result of me changing that line to debug and trying to change it back from memory. It's since been corrected and the problem persists. It did throw us for a loop however because we were getting results, but we figured it out shortly after I posted the code here. \$\endgroup\$ – John Moffitt Sep 22 '10 at 22:26

A few things to try:
1) Make sure your buffer is initialized this will tell you if 0x98 is coming from the buffer or somewhere else.
2) Disable interrupts when you are adding items to the buffer

  • \$\begingroup\$ Yes, disable interrupts that are related to a data structure while it is being modified. Otherwise you open yourself up to race conditions \$\endgroup\$ – ajs410 Sep 20 '10 at 17:21
  • \$\begingroup\$ The interrupt he was using to send the buffer he enables after the buffer has been filled. \$\endgroup\$ – Kortuk Sep 22 '10 at 14:03

I see nothing that makes your buffer circular. I think you're relying on an overflow to get you from 127 to 0...but an unsigned char can represent the values from 0 to 255. So your TXCurrentPos is going to walk right off the end of your array.

I've also seen the PIC C compilers do some weird stuff before for no good reason. Most compilers can also output the assembled version of the program; it might be worth some time to peruse the assembly listing and see how the C being compiled. Depending on the data type TXREG is declared as, the compiler might be trying to do a cast.

Also, in your receive code in the interrupt, you MUST check the receiver for an overrun condition and clear it. Every. Single. Time. The RS232 receiver will refuse to receive additional data while the overrun error bit is set.

void ClearRXOverRun()
    // If there's a frame overrun, RS232 will refuse to receive
    if (OERR == 1)
        // Clear overrun condition by turning RX off and back on
        CREN = 0;
        CREN = 1;   
  • \$\begingroup\$ The pastebin code, which has all of it does zero the counter. \$\endgroup\$ – Kortuk Sep 21 '10 at 1:53
  • \$\begingroup\$ It does? Where? \$\endgroup\$ – Peter Gibson Sep 21 '10 at 11:01
  • \$\begingroup\$ It's initialized to 0 at the top, but I did a ctrl-f for TXCurrentPos and I only found the initializer and the incrementer. \$\endgroup\$ – ajs410 Sep 21 '10 at 17:11
  • \$\begingroup\$ I thought I saw it, I do not have time to look now. Sorry if I am mistaken. \$\endgroup\$ – Kortuk Sep 22 '10 at 14:02
  • \$\begingroup\$ Click edit on your answer ajs410. Just change something super minor for me. \$\endgroup\$ – Kortuk Sep 22 '10 at 14:02

I'm sorry this might sound really simple, but have you tried writing your data to EEPROM, and then reading later, to verify you are in fact actually assigning the right data to the array? If the data that is getting assigned to the buffer isn't valid data then that might be your problem?


For this type of problem I divide the problem even further then I usually look at the assembly. Also I have seen problems where the RX could corrupt or overflow a buffer so I would disable the RX until you have the TX working.

The first thing I would do is move

if(waitingForACK == 0){
    PIE1bits.TXIE = 1; //Enable the TX interrupt

out of addToBuffer to is own function. Then call it when the buffer is full not as you go. It could be some timing problem where you are not able to add while the interrupt is sending out the data. When I transmit a packet I usually disable TX interrupt, fill up TX buffer and then enable the TX interrupt.

Then test the circular buffer outside of the interrupt. In you main function fill up the buffer then read out the data out.

Then next change your interrupt code to go through an intermediate volatile. So you can put a break point in your interrupt and see what is happing. The volatile is so that the complier does not remove the temp variable.

volatile unsigned char temp;
if((TXEndPos - TXCurrentPos) > 0)            // if there is data in the buffer
    temp  = TXBuffer[TXCurrentPos]; 
    Nop();                                   // for adding breakpoints
    // add error checking to make sure TXREG is empty.
    TXREG = temp;                            // send next byte
    TXCurrentPos = TXCurrentPos + 0x01;      // update to the new position

Another small thing is that I would also look into changing

else if (PIR1bits.TXIF == 1){


if (PIR1bits.TXIF == 1){

to insure that the other interrupts are not starving the TX interrupt.

Then if you still have the problem look at the assembly code I have seen where the complier does something weird and it is usually obvious if you look at the assembly.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy