|
|
View previous topic :: View next topic |
Author |
Message |
tyro
Joined: 25 May 2005 Posts: 10
|
PROBLEMS with SOFTWARE USART ON PIC16F876! PLEASE HELP! |
Posted: Wed May 25, 2005 3:27 pm |
|
|
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
|
|
Posted: Wed May 25, 2005 4:22 pm |
|
|
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
|
|
Posted: Wed May 25, 2005 4:44 pm |
|
|
Oh sorry the are wrong, yeah. I mean . 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
|
|
Posted: Wed May 25, 2005 5:03 pm |
|
|
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
|
|
Posted: Wed May 25, 2005 5:07 pm |
|
|
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
|
|
Posted: Thu May 26, 2005 8:04 am |
|
|
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
|
|
Posted: Thu May 26, 2005 8:54 am |
|
|
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
|
|
Posted: Thu May 26, 2005 9:41 am |
|
|
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
|
|
Posted: Thu May 26, 2005 10:24 am |
|
|
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
|
|
Posted: Thu May 26, 2005 11:03 am |
|
|
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
|
|
Posted: Thu May 26, 2005 12:34 pm |
|
|
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.
I will need fewer cycles. |
|
|
Ttelmah Guest
|
|
Posted: Thu May 26, 2005 1:58 pm |
|
|
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
|
|
Posted: Thu May 26, 2005 2:28 pm |
|
|
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
|
|
Posted: Thu May 26, 2005 5:50 pm |
|
|
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
|
|
Posted: Fri May 27, 2005 2:52 am |
|
|
Data is sent LSB first, so shift right is correct.
Best Wishes |
|
|
|
|
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
|