|
|
View previous topic :: View next topic |
Author |
Message |
dvmaster
Joined: 14 May 2016 Posts: 3
|
Faulty communication between two Pics 16f877a |
Posted: Sat May 14, 2016 12:43 am |
|
|
Hey guys, so I've been programing pics for two weeks now, and just today I ran into a problem.
In one of the PICS I read a value from a potentiometer using the ADC and then send it to the other PIC using RS232, however when I built the circuit I only receive the potentiometer values a few times before the whole thing stops working, and by that I mean they both freeze and I can't even restart them using the MCLR.
Here's both codes:
Transmitter
Code: |
#include <16F877a.h>
#device adc=10
#use delay (crystal = 4mhz)
#fuses PUT, HS, NOWDT, NOLVP, NOBROWNOUT,XT
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, errors)
int16 digital_reading;
double angle;
int8 *p;
void main()
{
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(RA0_ANALOG); // set PIN_A0 as analog input channel
set_adc_channel(0); // point ADC to channel 0 for ADC reading
delay_ms(5000);
while(true)
{
digital_reading = read_adc();
delay_ms(100);
angle = digital_reading;
p = ∠ // pointer p points to the first byte of f
putc(p[0]); // use p as an array Send the first byte
putc(p[1]); // Send the second byte
putc(p[2]); // Send the third byte
putc(p[3]); // Send the forth byte
putc(p[4]); // Send the fifth byte
putc(p[5]); // Send the sixth byte
putc(p[6]); // Send the seventh byte
putc(p[7]); // Send the eighth byte
delay_ms (2000);
}
}
|
Receiver
Code: |
#include <16F877a.h>
#fuses PUT, HS, NOWDT, NOLVP, NOBROWNOUT, XT
#use delay (crystal = 4mhz)
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D4
#define LCD_DATA5 PIN_D5
#define LCD_DATA6 PIN_D6
#define LCD_DATA7 PIN_D7
#include <lcd.c>
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, errors)
double f;
int8 *p;
#int_rda
void rd_isr(void)//función de interrupción por recepción de datos USART
{
p = &f;
p[0] = getc();
p[1] = getc();
p[2] = getc();
p[3] = getc();
p[4] = getc();
p[5] = getc();
p[6] = getc();
p[7] = getc();
}
void main()
{
enable_interrupts(global);//Habilito interrupción USART
enable_interrupts(int_rda);
lcd_init ();
printf (LCD_PUTC, "\f 08/05/2016\nUPIITA CONTROL");
delay_ms (500);
printf (LCD_PUTC, "\f");
while(true)
{
f=f/50;
LCD_Gotoxy (1, 1) ;
printf (LCD_PUTC, "A=%f", f);
delay_ms(500);
LCD_Gotoxy (1, 2) ;
printf (LCD_PUTC, "Ok");
delay_ms(500);
LCD_Gotoxy (1, 2) ;
printf (LCD_PUTC, " ");
}
}
|
As you can see it's pretty basic code, as I said I've only been doing this for a couple weeks so please bear with me. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19510
|
|
Posted: Sat May 14, 2016 1:36 am |
|
|
Key big thing to understand, is that INT_RDA, means _one_ character is waiting to be received. Just one. You should not be trying to receive multiple characters in the routine.
Then you have the problem that if anything is ever missed (communications are not perfect), there is nothing to synchronise the data at each end.
Then you have the stupidity (sorry!...), of using float.
The value you have is an integer. 0 to 1023. Nothing more. You try to use 'double', but your chip doesn't actually even support a double type (it will just be treated as single).
Treat float values, as if they carried a strong risk of infection!. 99.9% of jobs should never use them. Even if you wanted to do some maths, to get an angle think instead of integer 'milli-degrees'.
So, send the value, as an integer. I'd suggest hex. The you can use a 'line feed' character to synchronise the data. Just four characters needed to send, not eight.
You also have two oscillators selected. Get rid of 'HS' - this is for crystals above 4MHz. Just XT should be there.
Then read the data sheet. Is 'ADC_CLOCK_INTERNAL' recommended at 4MHz?.
So:
TX
Code: |
#include <16F877a.h>
#device adc=10
#use delay (crystal = 4mhz)
#fuses PUT, NOWDT, NOLVP, NOBROWNOUT,XT
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, errors)
int16 digital_reading;
void main()
{
setup_adc(ADC_CLOCK_DIV_8); //look at the data sheet
setup_adc_ports(RA0_ANALOG); // set PIN_A0 as analog input channel
set_adc_channel(0); // point ADC to channel 0 for ADC reading
delay_ms(5000);
while(true)
{
digital_reading = read_adc();
printf("%03LX\n",digital_reading); //send as 3 hex digits + LF
delay_ms (2000);
}
}
|
RX
Code: |
#include <16F877a.h>
#fuses PUT, NOWDT, NOLVP, NOBROWNOUT, XT
#use delay (crystal = 4mhz)
#define LCD_RS_PIN PIN_D0
#define LCD_RW_PIN PIN_D1
#define LCD_ENABLE_PIN PIN_D2
#define LCD_DATA4 PIN_D4
#define LCD_DATA5 PIN_D5
#define LCD_DATA6 PIN_D6
#define LCD_DATA7 PIN_D7
#include <lcd.c>
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, bits=8, parity=N, errors)
int16 reading=0;
int1 updated=FALSE;
#int_rda
void rd_isr(void)//función de interrupción por recepción de datos USART
{
static int16 value=0;
int8 rx_chr;
rx_chr=getc(); //Just[u] one [/u]character
if (rx_chr=='\n')
{
//have received the end of line
reading=value;
value=0;
updated = TRUE; //tell the code the value has updated
}
else
{
//For each hex digit, just add to the cumulative value*16.
value*=16;
value+=(rx_chr<='9')?rx_chr-'0':rx_chr-'A'+10;
//This is a horrible little C line to convert a hex digit to it's
//numeric value.
}
}
void main()
{
int16 scaled;
enable_interrupts(global);//Habilito interrupción USART
enable_interrupts(int_rda);
lcd_init ();
printf (LCD_PUTC, "\f 08/05/2016\nUPIITA CONTROL");
delay_ms (500);
printf (LCD_PUTC, "\f");
while(true)
{
//Only change the display when a new value arrives
if (updated)
{
scaled=reading*2;
updated=FALSE;
LCD_Gotoxy (1, 1) ;
printf (LCD_PUTC, "A=%06.2Lw",scaled);
}
}
}
|
Now, the receiver, doesn't delay. It will display the data _when it arrives_.
Then instead of dividing by 50, I'm multiplying by 2, and then displaying this as a value/100. %w, allows you to display an integer value, as a value with a decimal point. In this case xxx.xx. Two decimals, so equivalent to dividing by 100. Multiply by 2 first, gives the same result as your float maths, but literally hundreds of times faster.
Your master code sends a value every two seconds, so the slave needs to be sitting 'ready' for this.
Using 'hex' means the code can look for the line feed, and 'know' a value has arrived. Sending the raw binary values from a float, means that if a character ever gets missed, there is nothing to synchronise the transmission, and it'll not recover....
Brings the downside of the horrible conversion line from ASCII hex to numeric though... |
|
|
dvmaster
Joined: 14 May 2016 Posts: 3
|
|
Posted: Sat May 14, 2016 2:00 pm |
|
|
Hey man thanks a lot for your help, In a way it makes me feel retarded for making so many mistakes.
But I still cant get values from the pic.
It's like it never get's a value from the other pic, because it dosen't even displays what's inside the if(updated) so updated never changes value from the original false.
I've tried the following:
- Switching pics and programming each one with both codes. To make sure they both work. Even programmed them with some other code. They both work.
- Made sure Tx from the transmitter is connected to Rx of the receiver and the other way around. So basically 25 to 26 and 26 to 25.
It's like the transmitter program won't even start running. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Sat May 14, 2016 3:11 pm |
|
|
re: transmitter
put an LED and 470r on an unused I/O pin and have it 'toggled' every time through the main loop. That way you KNOW the program is running.
you can do the same for the receive program. just 'toggle' an LED within the
if ( updated) .....
portion of code.
Jay |
|
|
dvmaster
Joined: 14 May 2016 Posts: 3
|
|
Posted: Sat May 14, 2016 7:21 pm |
|
|
Good news, I've managed to solve it, with the codes provided.
There probably was also something sketchy with the protoboard I was using since buiding the circuit on a different one made it work.
Thanks everyone. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19510
|
|
Posted: Sun May 15, 2016 12:41 am |
|
|
Good.
I took the time to give a pretty 'full' description on this, because it is a very good place to start, and a lot of people can now use this as a description about this.
You also asked clearly and well, which always helps us.
There are other reasons too. You will see 'ADC_CLOCK_INTERNAL', even used on some of the CCS examples. Duh. The data sheet really has to be your friend. Then trying to receive multiple bytes in INT_RDA, is another very common problem. We normally say 'look at ex_sisr.c' for the classic 'how to use the interrupt' example.
You had quite a lot of things 'right', so showed that you were trying (you will see we get a lot of posters who are not...).
So have fun moving ahead from here. |
|
|
|
|
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
|