![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
can_putd() troubles - SOLVED |
Posted: Fri Nov 19, 2010 11:25 pm |
|
|
I'm using a PIC18F4685 (running at 40MHz) to send 256 CAN frames to another device on the CanBUS. The way the send routine works is that a loop runs 256 times calling the can_put() function each time.
If the PIC node and the target node are the only devices present on the bus everything works well. However, if other nodes are connected and they are actively talking on the bus problems arise. Using a CAN monitor I can see that sometimes the PIC sends out two CAN frames out of sequence. It sends the second frame first and the first frame second.
My theory was that the can_putd() wasn't successfully completing a transmission because the bus is busy before the loop ran again and made a new can_putd() call.
Looking at the CCS can_putd() function I see no code that forces it to wait for the buffer to show empty once the transmission is successfully completed. Looking at the PIC18F4685 datasheet on Page 288, the ASM example clearly shows a test of the TXREQ bit in the TXB0CON register to make sure the message was successfully sent before moving on.
So I added the following statement to the can_putd() function:
Code: | while(TXBaCON.txreq); // Wait for TXREQ bit to be cleared. This indicates the message was successfully sent |
The program hangs the first time I try to send a CAN frame (it's getting stuck inside the while loop).
I then tried checking the TXBIF bit instead:
Code: | while(!(TXBaCON.txbif)); // Wait for TXBIF bit to get set. This indicates the message was successfully sent |
The program again hangs the first time I try to send a CAN frame (it's getting stuck inside the while loop).
My current can_putd() function looks like this:
Code: | int1 can_putd(int32 id, int * data, int len, int priority, int1 ext, int1 rtr)
{
int i;
int * txd0;
int port;
txd0=&TXRXBaD0;
if (!TXB0CON.txreq)
{
CANCON.win=CAN_WIN_TX0;
port=0;
}
else if (!TXB1CON.txreq)
{
CANCON.win=CAN_WIN_TX1;
port=1;
}
else if (!TXB2CON.txreq)
{
CANCON.win=CAN_WIN_TX2;
port=2;
}
else return(0);
TXBaCON.txpri=priority;
can_set_id(TXRXBaID, id, ext);
TXBaDLC=len;
TXBaDLC.rtr=rtr;
for (i=0; i<len; i++)
{
*txd0=*data;
txd0++;
data++;
}
TXBaCON.txreq=1;
//Added to address concerns over message send ordering
//while((TXBaCON.txbif)==0); // Wait for TXREQ bit to clear. This indicates the message was successfully sent
CANCON.win=CAN_WIN_RX0;
return(1);
} |
Other strange things I've observed is that the variable gets set to 4, 3 or 2, but I don't see where it's ever used.
Also in the CCS register defines it looks like bugs are present (although as I said all this works as is):
Code: | struct txbNcon_struct
{
int txpri:2;
int1 void2;
int1 txreq;
int1 txerr;
int1 txlarb;
int1 txabt;
//int1 void7;
int1 txbif;
};
struct txbNcon_struct TXB0CON;
struct txbNcon_struct TXB1CON;
struct txbNcon_struct TXB2CON;
struct txbNcon_struct TXBaCON;
#byte TXB0CON=0xF40 //OK
#byte TXB1CON=0xF30 //OK
#byte TXB2CON=0xF20 //OK
//#byte TXBaCON=0xF60 //Original CCS code - 0xF60 is the address of RXB0CON - a possible bug
#byte TXBaCON=0xF40 //Shouldn't this be set to 0xF40 which is the address of TXB0CON???
//...
struct txbNdlc_struct
{
int dlc:4;
int void54:2;
int1 rtr;
int1 void7;
};
struct txbNdlc_struct TXB0DLC;
struct txbNdlc_struct TXB1DLC;
struct txbNdlc_struct TXB2DLC;
struct txbNdlc_struct TXBaDLC;
#byte TXB0DLC=0xF45 //OK
#byte TXB1DLC=0xF35 //OK
#byte TXB2DLC=0xF25 //OK
//#byte TXBaDLC=0xF65 //Original CCS code - 0xF65 is the address of RXB0DLC - a possible bug
#byte TXBaDLC=0xF45 ////Shouldn't this be set to 0xF45 which is the address of address of TXB0DLC
#byte TXRXBaEIDL=0xF64 //address of RXB0EIDL ???
struct
{
int void012:3;
int1 ext;
int1 srr;
int void567:3;
} TXRXBaSIDL;
#byte TXRXBaSIDL=0xF62 //address of RXB0SIDL ??? |
My Compiler version is PCH V4.083
If anyone has any comments or suggestions they would be greatly appreciated.
Rick
Last edited by RickMarsen on Wed Nov 24, 2010 1:24 am; edited 1 time in total |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Sat Nov 20, 2010 7:24 am |
|
|
I now see that Microchip's datasheet on Page 289 shows a different sending method that uses WIN bits and I see that the can_putd() function uses this approach. From this, it's clear why some register defines initially seemed wrong to me - I've changed them all back to default CCS 18xxxcan.c values.
My CANBus sending and receiving still works with no other nodes connected.
However, this Microchip code snippet from p.289 again shows polling the txreq bit to check if the message has been sent.
Code: | ; If required, wait for message to get transmitted
BTFSC RXB0CON, TXREQ ; Is it transmitted?
BRA $-2 |
I still get stuck in the while loop if I try to check the txreq bit near the end of the can_putd() function. |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Sat Nov 20, 2010 7:45 am |
|
|
If I move the while statement after this line
Code: | CANCON.win=CAN_WIN_RX0; |
I don't get stuck in the loop, but I am not sure if it's doing what I need it to do which is sit and wait for the current transmission to complete before it returns to the calling function.
My can_putd() now looks like this
Code: |
//...
for (i=0; i<len; i++)
{
*txd0=*data;
txd0++;
data++;
}
TXBaCON.txreq=1;
CANCON.win=CAN_WIN_RX0;
//Added to address concerns over message send ordering
while(TXBaCON.txreq); // Wait for TXREQ bit to clear. This indicates the message was successfully sent
return(1);
} |
I still don't understand why I'm not able to structure my can_putd() as per Microchip's code sample where the txreq bit is first polled and THEN the WIN bits get re-set to their default values. If I perform the steps in the Microchip-recommeded sequence I get stuck in the while() loop. I think my problem stems from this bank-switching using win bits - maybe I am looking at the wrong register when doing the checking???.
Code: | ///...
; Now that all data bytes are loaded, mark it for transmission.
MOVLW B’00001000’ ; Normal priority; Request transmission
MOVWF RXB0CON
; If required, wait for message to get transmitted
BTFSC RXB0CON, TXREQ ; Is it transmitted?
BRA $-2 ; No. Continue to wait...
; Message is transmitted.
; If required, reset the WIN bits to default state. |
|
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Nov 20, 2010 9:31 pm |
|
|
My feeling is if you want help on this, you should post a description of
a test setup that we can reproduce on our bench, using 3 PIC boards.
This would include the length of the cables between the boards (it should
fit on a bench), and the location of termination resistors, and the part
number of the CAN bus transceiver chips, etc.
Also, post three very short test programs for these boards that will
demonstrate the problem. Examples of test programs:
In the thread below, I made one program for one board that generates
the test signals. Then I made a 2nd program to measure them.
http://www.ccsinfo.com/forum/viewtopic.php?t=43892&start=9
Here are some test programs for two CAN bus boards:
http://www.ccsinfo.com/forum/viewtopic.php?t=43903&start=14
Notice how they are short and to the point. There is no CCS Wizard code
in the programs. These are examples of what I'd like you to write, in
order to show the problem. The programs you post should be complete,
and ready to compile with no errors.
It's unlikely that we will have your CAN bus analyzer. So, the problem
has to be demonstrated in hardware in such a way that no analyzer is
needed. We need to be able to see the problem without it. And also, if
we cure the problem, we need to be able to see that it's been fixed, with
no analyzer - just the 3 boards and some RS232 output to a terminal.
Question: Have you tried any other type of PIC (with CAN bus module),
other than the 18F4685 ? The problem may be dependent on the PIC type. |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Sun Nov 21, 2010 10:33 am |
|
|
PCM,
I will prepare and post some test code and a brief description of the hardware set-up, as you suggested.
I have not tried any other ECAN enabled PICs. In fact, this one works quite well (both sending and receiving) as is, provided IT and the receiving CAN node are the only nodes on the bus. If they are not alone on the bus, and the bus is "teeming" with other traffic the problem I observe (using a separate listen-only CAN monitor) is that the PIC appears to randomly send out two CAN frames out of sequence. Say I am sending a repeating sequence of CAN frames with the 8-byte payload alternating between all AAs and all BBs and I am inserting a 1ms delay between each packet. Instead of seeing AA BB AA BB AA BB on the bus, I sometimes observe AA BB AA AA BB or BB AA BB BB AA. The other thing to note is that AA AA or BB BB pairs are only 2-3us apart, not 1ms as called for by the delay_ms(1) delay inside the main function loop from which the can_put() function is being called.
I don't think the problem is electrical (such as termination, cable length). I think it has to do with halting the next CAN packet loading and transmission before the current one is released. I am picturing this mechanism like torpedo launcher tubes on a submarine.
So far I have not figured out how to do that. Microchip shows how to do this by polling the TXREQ bit in TXBnCON, but I can't see it being done anywhere in the CCS can_putd(), nor have I been able to modify the can_putd() to add this functionality.
I had a similar problem with RS485 a long time ago where I would put the RS485 transceiver into TX mode, send out the byte and then turn the direction back around to RX in the next line of code. The trouble was, I was changing the transceiver direction in the middle of the UART byte transmission so the messages were never making it to the other end uncorrupted. This was solved by adding polling just before the transceiver direction got switched back to the RX mode:
Code: | while(bit_test(*0xFAC,1) == 0); // Wait for TSR Register to empty by checking Bit 1 (TRMT) in TXSTA (1=EMPTY, 0=FULL) |
I am convinced my problem here would be solved by preventing the can_putd() function from returning before it made sure the current CAN packet successfully made it out. I just haven't figured out how to do that yet. |
|
![](templates/subSilver/images/spacer.gif) |
collink
Joined: 08 Jan 2010 Posts: 137 Location: Michigan
|
|
Posted: Mon Nov 22, 2010 6:42 am |
|
|
RickMarsen wrote: |
I am convinced my prblem here would be solved by preventing the can_putd() function from returning before it made sure the current CAN packet successfully made it out. I just haven't figured out how to do that yet. |
DO NOT DO THIS!
Your problem is that the canbus hardware in your PIC chip has three transmit slots. can_putd() just finds an open slot (if one exists) and puts the frame to send into that slot. I think that you are assuming that can_putd actually sends the data over canbus but it does not. The hardware will try to send from the transmit buffers all by itself without you having to do anything else. This happens behind your back basically. You are getting out of order sending because you have multiple frames within your transmit buffers and the hardware is trying its best to sent out of all of the buffers. Sometimes it might send out of order while doing this. This is how it is supposed to work. You are supposed to put frames into the buffer and the hardware is supposed to send from the buffers without anything further from you.
You should not call can_putd if there is not currently an open buffer. The CCS canbus routines you are using should have a define called "can_tbe". This define checks whether a buffer is open. You use it like so:
Code: |
if (can_tbe()) can_putd <parameters to the transmit function>
|
Basically, if you really need to send in order then this is what you do:
Create a define that just checks the first transmit buffer:
Code: |
#define can_tbe_0() (!TXB0CON.txreq)
|
Then, when you want to send you check that buffer to see if it's clear. If it is you send the next piece of data (you have it in an array I assume?). Just increment a counter.
Code: |
if (can_tbe_0()) can_putd(id, &data[counter++], length, priority, type, rtr);
|
Otherwise you can use all three transmit buffers and still increment the counter but with all three buffers in play you are likely to get out of order sending. |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Tue Nov 23, 2010 6:18 pm |
|
|
Collin,
Your response makes perfect sense to me. I will try it and let you all know how it goes.
Do you see any issues with using your suggestion in a slightly different manner:
- Add the following statement to can_putd() just ahead of the first "if" statement (the if statement that directs the incoming data to the first free buffer).
Code: | while(TXB0CON.txreq); // wait for the first TX buffer to be clear |
Rick |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Wed Nov 24, 2010 1:23 am |
|
|
Collin,
Your suggestion worked. I wasn't able to mod the can_putd() function using a polling while loop - it would always get stuck there.
I used a tbe define as you suggested and placed an if around my calls to can_putd() and the packets are now always in sequence.
Thank you and PCM for taking a look at this and taking the time to respond.
Rick |
|
![](templates/subSilver/images/spacer.gif) |
RickMarsen
Joined: 31 Oct 2008 Posts: 17
|
|
Posted: Wed Nov 24, 2010 7:14 am |
|
|
I should also mention that although I have a working solution, I still can't figure our why one of these methods works and the other does not:
This method does not work (program gets stuck in the while loop):
Code: | while(TXB0CON.txreq);
can_putd(id, &data[counter++], length, priority, type, rtr);
|
but this method works
Code: | #define can_tbe_0() (!TXB0CON.txreq)
#define NO 0
#define YES 1
int1 CANsent = NO;
//...
while(CANsent==NO)
{
if (can_tbe_0())
{
can_putd(id, &data[counter], length, priority, type, rtr);
counter++;
CANsent = YES;
}
}
CANsent=NO; |
|
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|