CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

PROBLEMS with SOFTWARE USART ON PIC16F876! PLEASE HELP!
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PROBLEMS with SOFTWARE USART ON PIC16F876! PLEASE HELP!
PostPosted: Wed May 25, 2005 3:27 pm     Reply with quote

Hello. I have a problem sending and getting BYTES from PIC to Hyperterminal and otherwise with my own SOFTWARE SERIAL functions. With put_char() a byte is only sent right at 9600 bps. with get_char() nothing is incoming right at any speed. I need both functions at 115kbps! Iīve also tried a lot with the timing (delay_cycles(24) --> 1bit at 115k), but nothing works.

Can somebody help me?

Here is my code:

Code:
#include<16F873.h>
#include<stdlib.h>

#fuses HS,NOWDT,PUT,NOPROTECT,NOBROWNOUT,NOLVP

#use delay(clock=11059200)
#use rs232(baud=115200,rcv=PIN_C7,xmit=PIN_C6,parity=n,bits=8,ERRORS,STREAM=PC_STREAM,FORCE_SW)

#define PORTA 0x05
#define PORTB 0x06
#define PORTC 0x07

#define low 0
#define high 1

int i;

struct port_c{
BOOLEAN unused:6;
BOOLEAN TX;
BOOLEAN RX;}c;
#byte c=PORTc


//-----------------------------------------------------------------

void put_char(char character){

     c.tx=0; // Startbit
     delay_cycles(24);

     for(i=0; i<=7; i++){
     output_bit(PIN_C6,shift_right(&character,1,0));
     delay_cycles(24);}

     c.tx=1; // Stopbit
     delay_cycles(24);}


//-----------------------------------------------------------------

BYTE get_char(){

BYTE character_2;

LCD_BLANK();
lcd_putc('\1');
lcd_putc("Waiting for char.");

for(;;){
while(1){
if(kbhit(PC_STREAM)){delay_cycles(24); break;}} // If Startbit

if(!input(PIN_C7)){ // If Startbit
delay_cycles(24);}
else continue;

for(i=0; i<8; ++i){ // Data (8 Bit)
shift_left(&character_2,1,input(PIN_C7));
delay_cycles(24);}

if(input(PIN_C7)){ // Stopbit
delay_cycles(24);
return character_2;}}}


//-----------------------------------------------------------------

void main(){

BYTE b;

set_tris_c(0x82);

setup_adc(ADC_OFF);
setup_adc_ports(NO_ANALOGS); // Make PORT A digital

while(1){
delay_ms(500);
put_char('R');}

//while(1)
//b=get_char();
}


Last edited by tyro on Wed May 25, 2005 4:54 pm; edited 3 times in total
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Wed May 25, 2005 4:22 pm     Reply with quote

First of all, why are you not using the CCS supplied software serial functions?

As you already figured out at 11059200Hz a single bit takes 24 instruction cycles, not 24us. Replace delay_us() by delay_cycles().

The main problem in your functions is timing. Have a look at the generated assembly code for your functions in the generated *.lst file and you'll see there is some overhead to be compensated for. Count all the assembly instructions and make corrections for this in the delay_cycles() calls. Remember that jump instructions take two instruction cycles.

Code:
for(i=0; i<=8; ++i){ // Data (8 Bit)

I haven't checked the compiled code, but I think this equals to
Code:
for(i=0; i<=8; i++){ // Data (8 Bit)
which counts 9 bits, not 8.

Please use the 'Code' button when posting source code, this will ensure the formatting is preserved and makes for easier reading.
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PostPosted: Wed May 25, 2005 4:44 pm     Reply with quote

Oh sorry the
Code:
delay_us(24)
are wrong, yeah. I mean
Code:
delay_cycles(24)
. I have tried this, too.

The reason why I dont use fputc, putc, fgetc, getc, etc.: I have tried all these functions and they donīt work.

one "for-loop" was wrong! But I have tested it with 8 data bits, too. I "have" output at Hyperterminal, but the wrong characters.

OK I HAVE CHECKED AND CORRECTED THE CODE ABOVE!


Last edited by tyro on Wed May 25, 2005 5:04 pm; edited 1 time in total
rwyoung



Joined: 12 Nov 2003
Posts: 563
Location: Lawrence, KS USA

View user's profile Send private message Send e-mail

PostPosted: Wed May 25, 2005 5:03 pm     Reply with quote

tyro wrote:
Because i "have" output at Hyperterminal, but the wrong characters.


Sounds suspiciously like you are NOT using an RS-232 level converting chip. If this is the case then you need to invert the bits yourself in your routines or read the documentation for #use rs232 in the manual.
_________________
Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month!
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PostPosted: Wed May 25, 2005 5:07 pm     Reply with quote

No, the problem is, i need 115kbps connection inverted on PIN_C6 and PIN_C7. Because the circuit board is already made. I have realized too late that i need inverted output to control the IR transceiver on this board.
rwyoung



Joined: 12 Nov 2003
Posts: 563
Location: Lawrence, KS USA

View user's profile Send private message Send e-mail

PostPosted: Thu May 26, 2005 8:04 am     Reply with quote

tyro wrote:
No, the problem is, i need 115kbps connection inverted on PIN_C6 and PIN_C7. Because the circuit board is already made. I have realized too late that i need inverted output to control the IR transceiver on this board.


And you tried the INVERT option in your #use rs232 setup? And that didn't work?

115.2kbps with an 11.0592MHz xtal should work just fine in the hardware uart. That is one of the magic combinations where the SPBRG value is a nice integer value.

This is about as simple a program as I can envision for testing. If necessary, add the "invert" option to the #use rs232 line if you do NOT have something like a MAX232 chip driving your RS232 port.

By the way, I typed this in free-hand so keep that in mind if there are any syntax errors.

Code:

#include <16F873.h>
#case
#fuses HS, NOWDT, PUT, NOPROTECT, NOBROWNOUT, NOLVP
#use delay(clock=11059200)
#use rs232(baud=115200, rcv=PIN_C7, xmit=PIN_C6, parity=n, bits=8, errors, force_sw)
// using "standard io" by default so compiler gets to set TRIS registers
void main(void)
{
   int8 cmd='a';
   setup_adc(ADC_OFF);
   setup_adc_ports(NO_ANALOGS);
   while(1)
   {
      putc(cmd++);
      if (cmd > 'z') cmd = 'a';
      delay_ms(500);
   }
}

_________________
Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month!


Last edited by rwyoung on Thu May 26, 2005 11:00 am; edited 1 time in total
Ttelmah
Guest







PostPosted: Thu May 26, 2005 8:54 am     Reply with quote

The problem here is that you cannot 'invert' the hardware UART.
You should still be able to use an inverted software UART, on the hardware UART pins, by using the 'FORCE_SW' command, and 'INVERT'.
This should work fine, but you need both commands in the #USE RS232, for it to work.

Best Wishes
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PostPosted: Thu May 26, 2005 9:41 am     Reply with quote

No, no, no. This is not the problem. I must INVERT my output. Let me explain:

The old board I have, is only PIC16f876 communicating wired with Nokia. The software with hardware usart works fine at 115k without inverting.

Now I have a PIC16f876 on a new board with an TFDU4100 IR-transceiver, who should communicate with Nokia handy wireless.

But now with IR-transceiver I must invert the pic in-/output, because I noticed too late that the RX/TX must be inverted with this IR transceiver.

Therefore Iīve tried to put FORCE_SW and INVERT in #use rs232 and to use fputc() & fgetc() again. But it didnīt work.

This is the reason, why I wrote my own software serial functions.

I have tested my put_char() at 9600 bps with PIC <-MAX232-> Hyperterminal and it works fine. At faster speeds with the right delays it doesnīt work.

I only want these functions to work fine.
Ttelmah
Guest







PostPosted: Thu May 26, 2005 10:24 am     Reply with quote

I am very suprised the internal functions don't work for you, since they are generally reliable.
One thing that is very 'strange' about the code you post, is that it does _not_ properly invert the data. I am almost suspicious, that you misunderstand the polarity of serial communications. 'TTl' (well CMOS for the PIC) serial (as at the PIC), idles high. The 'stop' bit is a 5v level, while the 'start' bit is 0v. These levels are then increased in ampiltude, and 'inverted', when transmitted using RS232. As such the TTL levels agree with what you are trying to transmit.
'Inverted' TTL serial, idles at 0v, and starts with a 5v level.
Have you tried the internal routines, without using invert?...

That having been said, the problem on your code, is that with higher baud rates, getting the timing calculations 'right', involves including every instruction in the calculation. Basically in your transmit loop, you need to compile the function using 'delay_cycles(1)', look at the list file, and count the instructions involved in the loop. I'd expect that the 'for' itelf, will use about four instruction times, the test and jump, another three, and the shift, probably half a dozen instruction times. These then need to be subtracted from the delay value put into your loop. At 9600bps, a timing 'error' of perhaps 4uSec, won't be significant, but as the rate rises, you need to compensate for the overheads.

Best Wishes
rwyoung



Joined: 12 Nov 2003
Posts: 563
Location: Lawrence, KS USA

View user's profile Send private message Send e-mail

PostPosted: Thu May 26, 2005 11:03 am     Reply with quote

RJ is right, trying to do your own software uart in C is going to come out wrong at high speeds.

Go back to the CCS software uart, use the INVERT command.

Use an oscilloscope to look at the signal on your IR module's Txd pin and also its IRED cathode pin. Do they make sense? Are the timings right?

You do have the limiting resistor selected properly, right? Can you detect anything coming out of the module (with either another module or by looking at the IRED cathode pin)?
_________________
Rob Young
The Screw-Up Fairy may just visit you but he has crashed on my couch for the last month!
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PostPosted: Thu May 26, 2005 12:34 pm     Reply with quote

This MUST BE the right way of using start and stopbit. You can see here: www.winpicprog.co.uk/pic_tutorial7.htm

The reason iīm not using INVERT in the example above is, I wrote that code for hyperterminal usage, not for acting with the IR transceiver. Its only a put_char()-get_char() test.

This code leaves PIC and is inverted by the max232 and back in PC.

But on my board, transceiving with nokia phone, the pic inverts the signal and IR transceiver inverts back, so its sent absolutely right, like the pic is wired directly to the Nokia phone.

Thank you for the idea with the many instruction cycles, the pic needs in loops. i will check that in the listfile!!!! Thanks. Very Happy
I will need fewer cycles.
Ttelmah
Guest







PostPosted: Thu May 26, 2005 1:58 pm     Reply with quote

So you post an example, and in fact have sent code that differs from what you are trying. A great way to ask questions, and also a good way to generate errors.
If you want to do this with reversable bits, for future reference, use the #if ability of the compiler. So:
Code:

#define INVERT
#ifdef INVERT
   #define START (1)
   #define STOP (0)
#else
   #define START (0)
   #define STOP (1)
#endif
#define BITTIME (24)

void put_char(char character) {
#ifdef INVERT
  character=~character;
#endif
  c.tx=START;
  delay_cycles(BITTIME-11);
  for (i=0; i<=7; i++) {
      output_bit(PIN_C6,shift_right(&character,1,0));
      delay_cycles(BITTIME-14);
  }
  delay_cycles(5);
  c.tx=STOP;
  delay_cycles(BITTIME-1);
}

This way the code remains 'as typed', when you change for inverted operation, and one source of potential problems is removed.
Posting code that is different from what you are actually asking the question about, is a sure way of wasting a lot of people's time.

Now the timings in the above are 'guesses', but point out the sort of changes needed. First, it takes time to execute each output instruction. This has to be subtracted from every delay. Similarly, the actual 'start' of the for loop, takes time and this has to be subtracted from the first delay.
Worse though (big problem at high data rates), the 'output_bit' instruction takes different times, if a bit is a '1' or a '0', with two extra cycles added if the bit is a '1'. Seperately, when the loop is exited, the time for this slower output instruction, is not incurred, so there has to be an extra delay to make up for this before the stop bit.
The thing that still has to be questioned is why it does not work with the standard routines. These implement almost exactly what is being shown here, with the bit time calculated from the clock rate, and the code in assembler. The only reasons for them failing, are a couple of odd compiler versions, which were quickly dropped, an incorrect clock definition, or selecting a rate that actually cannot be done in the loop accurately enough for the given clock.
On your receive code, just program your own test on the input bit going low. When it does, wait for 1.5 bit times (sampling should normally be in the centre of the incoming data bit), and again compensate for delays. You do not have to wait for the stopbit (doing so, and then delaying for a full bit time will inherently result in you being late for the start of the next byte). Check for the level going high, and wait for half a bit time, then return to the check for the input bit dropping.

Best Wishes
tyro



Joined: 25 May 2005
Posts: 10

View user's profile Send private message Send e-mail Visit poster's website ICQ Number

PostPosted: Thu May 26, 2005 2:28 pm     Reply with quote

Oh thanks Ttelmah for your code. It looks good. I will try it tomorrow. I know not exactly what you mean with the receive code. Could you be so nice and put an example of an get_char function in here, like the put_char of you.

Thanks a lot
ckielstra



Joined: 18 Mar 2004
Posts: 3680
Location: The Netherlands

View user's profile Send private message

PostPosted: Thu May 26, 2005 5:50 pm     Reply with quote

At least one more error in your code.
Code:
void put_char(char character){
,
,
     for(i=0; i<=7; i++){
     output_bit(PIN_C6,shift_right(&character,1,0));
.
.
.

BYTE get_char(){
.
.
.
for(i=0; i<8; ++i){ // Data (8 Bit)
shift_left(&character_2,1,input(PIN_C7));

Assume transmitting data byte '87654321. Sending the byte with shift_right() will transmit the data in the sequence '12345678'. Receiving with shift_left() will result in '12345678' being stored, effectively reversing the bit-sequence. This can be solved by using the same shift function for the send and receive, I'm just too tired right now to figure out whether the left or right shift is the correct one.

Writing your own send and transmit functions is not recommended as a new compiler version or different compiler options (optimization!) might create different assembly code. The required delay-loops would have to be manually checked before every release again and again...

Why don't you concentrate on the CCS provided software UART functions not working for you?
Which compiler version are you using?
Ttelmah
Guest







PostPosted: Fri May 27, 2005 2:52 am     Reply with quote

Data is sent LSB first, so shift right is correct.

Best Wishes
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
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