|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Apr 28, 2017 4:03 pm |
|
|
Quote: | I will post my code when I have it reduced. |
And a link to your board's schematic. Or at least the part showing all
connections to the DS3695 drivers, and their connections to the PIC. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9133 Location: Greensville,Ontario
|
|
Posted: Fri Apr 28, 2017 6:05 pm |
|
|
just an observation....
One thing not mentioned yet is that you're using RS-485 transceivers and some designs call for both pullup and pulldown resistors as well as the 120r connecting A to B.
Jay |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Sat Apr 29, 2017 2:46 am |
|
|
Temtronic is right on this. Some buffers will assume an unbiased line is 'idle' others will not. It is always safer to explicitly bias the bus.
However I think the problem is still back to understanding the PIC.....
Putc, will wait until the hardware buffer can take the byte, But will return as soon as it is accepted. This does not mean the byte has been _sent_....
Transmission of the byte will have started, but it them takes 10 bit times to actually be sent (assuming 8bits no parity).
To wait for transmission to complete, you have multiple choices:
1) Test the shift register empty bit in the UART.
Code: |
#bit TRMT = getenv("BIT:TRMT)
//then
while (TRMT!=1)
;
//will wait for the byte to send
|
2) Let the compiler handle it for you.
This is why the compiler has the 'enable' pin definition in #use rs232. If you specify this, the compiler will set the defined pin 'high' when data is actually being transmitted. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Apr 30, 2017 10:27 am |
|
|
How do I send a link to my schematic or pictures?
I don't trust Facebook or Dropbox (which I have tried) and I don't have a webpage.
Here is the MASTER code for a test program, see the comment at the start for the behaviour with and without ERRORS.
Note that the slave works without ERRORS, the MASTER does not.
I'm not really using RS485. I am using a differential driver/receiver chip at each end. The MASTER sends a data stream then immediately switches to receive and waits for a reply. If it does not get one (times out) then it gors through the loop and sends again.
The SLAVE listens fro a special character (0x55) then replies with a data stream and synchronizes.
Disconnecting or turning off either end interrupts the communication and the ROV shuts down until the cable is re-connected or the unit turned back on. It works 100% as long as ERRORS is included. In use I disconnect the cable to reel out or reel in the cable. I wish I could post pictures here.
NOTE: MPLAB IDE v8.92, PCM v5.055
Code: | // CONSOLE FIRMWARE File Console PIC_1_test.c Console to control ROV & test communications //
// Warning: Scan switch must be set to Horiz when programming the PIC. RB3 is LVP //
// Last modified: 30 Apr 2017 //
// With "ERRORS" in the #use rs232 statement displays: //
// ROV DATA, No Timeout, Start Char = 55, Counter = n (incrementing), Battery 13.8v //
// Without "ERRORS" in the #use rs232 statement displays: //
// ROV DATA, Timeout Error, Start Char = 00, Counter = 0, Battery 0.0v //
/* Pre-processor directives */
#include <16F884.H>
#include <math.h>
#fuses INTRC_IO, NOWDT, PUT, NOPROTECT, BROWNOUT, MCLR, NOLVP
#use delay (clock=8000000)
#use rs232 (baud=9600, parity=N, bits=8, enable = pin_E2, xmit=pin_C6, rcv=pin_C7, ERRORS)
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#byte porta = getenv("SFR:PORTA")
#byte portb = getenv("SFR:PORTB")
#byte portc = getenv("SFR:PORTC")
#byte portd = getenv("SFR:PORTD")
#byte porte = getenv("SFR:PORTE")
// define I2C address
// #define consolePIC_2_WRT_ADDR 0X10 // Console slave PIC 2
// #define consolePIC_2_READ_ADDR 0x11
#define LCD_1 0x40 // LCD 1 0x20
// #define LCD_2 0x42 // LCD 2 0x21
// #define LCD_3 0x44 // LCD 3 0x22
// constants
#define buff_size 22 // characters per line plus one
// Global Variables
char LCD_WRT_ADDR = 0X40; // set to first LCD, 0x4e default
short timeout_error = 0; // for timed get_c()
// Function prototypes
char timed_getc (void); // get RS232 character, timed
void clear_LCD (void); // Clear LCD
void set_I2C_addr (char addr); // set the I2C address, 7 bit
void text_position (int line, int column); // set start for next text
void set_font (char size); // set font size
void send_str(char *buff); // Send string to LCD
void beep(void); // beep for 100 ms
// ***************The main function*********************
void main(void)
{
// setup ports
set_tris_a (0xff); // all inputs
set_tris_b (0xff); // all inputs
set_tris_c (0xff); // all inputs
set_tris_d (0xef); // all inputs except D4
set_tris_e (0xfb); // all inputs except E2
output_low(pin_D4); // sonarlert off
// declare variables
int i;
float volts = 0; // ROV Battery Voltage to 0
// #bit trmt = 0x98.1 // TXSTA(bit 1). Tx Shift Reg Status bit, 1=empty
char buff[buff_size];
// outgoing packets
const int max_s = 10; // array size
char packet_s [max_s]; // packets to send
#define ENQ 0x55 // Enquiry
#define start_s (packet_s [0]) // request to send
#define main_thr (packet_s [1])
#define counter (packet_s [2]) // counter for testing
#define stbd_thr (packet_s [3])
#define consws (packet_s [4])
#define joysws (packet_s [5])
#define rudd (packet_s [6])
#define elev (packet_s [7])
#define pmrot (packet_s [8])
#define smrot (packet_s [9])
// initialize outgoing packet array
start_s = ENQ; // Enquiry in the first position
counter = 0x00; // start counter at zero
// incoming packets
const int max_r = 10; // array size
char packet_r [max_r]; // packets to receive
#define ACK 0xaa // Acknowledge
#define start_r (packet_r [0])
#define compass (packet_r [1])
#define bat_volts (packet_r [2])
#define bat_amps (packet_r [3])
#define pitch (packet_r [4])
#define roll (packet_r [5])
#define status_1 (packet_r [6]) // return start_s (ENQ)
#define status_2 (packet_r [7]) // return counter
#define depth (packet_r [8])
#define dist_from_bottom (packet_r [9])
start_r = 0; // clear receive start char
bat_volts = 50;
status_1 = 60;
status_2 = 70;
// Start the alphanumeric display sequences
delay_ms(2000); // allow time for LCD to start
LCD_WRT_ADDR = LCD_1;
clear_LCD(); // clear LCD_1
delay_ms(5);
set_font(10); // change text to size 10
text_position(0,0); // start first line
sprintf(buff, "ROV DATA"); // place text string in buffer
send_str(buff); // display text array
// beep for 100 msec
beep();
// ******************** loop continuously ***************
while (1)
{
// Start of data transmission *******************************************************
for (i=0; i<max_s; i++) // 10 packets, 0 to 9
{
putc (packet_s [i]); // send the packet
} // end send packet for loop
// Timed Wait for reply
timeout_error = FALSE; // so WHILE is not skipped
while ((packet_r [0] != ACK)&&(!timeout_error)) // wait for ACK or timeout
packet_r [0] = timed_getc(); // in this loop
if (!timeout_error) // if no timeout error
{
for (i=1;i<max_r;i++) // get the remaining 6, or timeout
packet_r [i] = timed_getc(); // 1 to 6
}
// LCD #1, display
// Should display: No Timeout, Start Char = 0x55, Counter = n (n increments)
LCD_WRT_ADDR = LCD_1;
text_position(0,1); // start second line
if(timeout_error)
sprintf(buff, "Timeout Error"); // prepair to display
else
sprintf(buff, "No Timeout "); // if timeout or not
send_str(buff); // display one of the above
text_position(0,2); // start third line
sprintf(buff, "Start Char = %x", status_1); // display first received character
send_str(buff);
text_position(0,3); // start fourth line
sprintf(buff, "Counter = %3U ", status_2); // display counter
send_str(buff);
// adjust units and display battery status
volts = bat_volts * 0.0586; // scale battery voltage
text_position(0,6);
sprintf(buff, "Battery %2.1fv", volts); // display 13.8v to test
send_str(buff);
packet_r [0] = 0x00; // clear ACK
} // end of while loop
} // end of main function
// Functions
// function 'timed_getc' waits up to 50 ms for a character
char timed_getc()
{
long timeout;
timeout_error = FALSE;
timeout = 0;
while (!kbhit() && (++timeout<5000)) // 50 msec
delay_us(10);
if (kbhit())
return (getc());
else
{
timeout_error = TRUE;
return(0);
}
}
// Clear Display
void clear_LCD (void)
{
I2C_START (); // start I2C
I2C_WRITE (LCD_WRT_ADDR); // addr of LCD
I2C_WRITE ('C'); // C CL to clear display
I2C_WRITE ('L'); // L
I2C_STOP (); // stop I2C
}
// set the I2C address, 7 bit
void set_I2C_addr (char addr)
{
I2C_START (); // start I2C
I2C_WRITE (LCD_WRT_ADDR); // addr of LCD
I2C_WRITE ('S'); //
I2C_WRITE ('I'); //
I2C_WRITE ('2'); //
I2C_WRITE ('C'); //
I2C_WRITE ('A'); //
I2C_WRITE (addr); //
I2C_STOP (); // stop I2C
}
// set position of next text
void text_position(int line, int column)
{
I2C_START (); // start I2C
I2C_WRITE (LCD_WRT_ADDR); // addr of LCD
I2C_WRITE ('T'); // T, TP set text position
I2C_WRITE ('P'); // P
I2C_WRITE (line); // line position
I2C_WRITE (column); // column position
I2C_STOP (); // stop I2C
}
// Set Font Size
void set_font (char size)
{
I2C_START (); // start I2C
I2C_WRITE (LCD_WRT_ADDR); // addr of LCD
I2C_WRITE ('S');
I2C_WRITE ('F');
I2C_WRITE (size);
I2C_STOP (); // stop I2C
}
// send string to LCD
// void send_str(char buff[buff_size])
void send_str(char *buff)
{
int i;
I2C_START (); // start I2C
I2C_WRITE (LCD_WRT_ADDR); // addr of LCD
I2C_WRITE('T'); // send TT for text coming
I2C_WRITE('T');
for (i=0; i<buff_size; i++)
{
I2C_WRITE(buff[i]); // send string
}
I2C_WRITE(0); // send termination
I2C_STOP (); // stop I2C
delay_ms(30);
}
// beep for 100 ms
void beep()
{
output_high(pin_D4); // Sonarlert on
delay_ms(100);
output_low(pin_D4); // for 0.1 sec
}
// end
|
Here is the code for the slave.
Code: | /// MAIN THRUSTER FIRMWARE 28 Apr 2017 ///
// This version actually works without "ERRORS" in the #use rs232 statement
/* Pre-processor directives */
#include <16F874A.H>
#fuses HS, NOWDT, PUT, NOPROTECT
#use delay (clock=8000000)
#use rs232 (baud=9600, parity=N, bits=8, enable = pin_C5, xmit=PIN_C6, rcv=PIN_C7,ERRORS)
#use I2C (master, SCL=PIN_C3, SDA=PIN_C4)
#byte porta = getenv("SFR:PORTA")
#byte portb = getenv("SFR:PORTB")
#byte portc = getenv("SFR:PORTC")
#byte portd = getenv("SFR:PORTD")
#byte porte = getenv("SFR:PORTE")
// Global variables
short timeout_error = 0; // error for timed_getc
// Function prototypes
char timed_getc();
// ****************************************************************************
/* The main function */
void main(void)
{
// declare variables
int i;
// initialize
#define read_port 0xff // all bus pins input
#define write_port 0x00 // all bus pins output
// Receive Packet from surface
#define ENQ 0x55 // Enquiry
const int max_r = 10; // number of parameters to receive
char packet_r [max_r]; // size of receive packet array
#define start_r (packet_r [0]) // start byte
#define m_motor (packet_r [1]) // main motor speed & direction
#define counter (packet_r [2]) // use as counter
#define s_motor (packet_r [3]) // stbd motor speed & direction
#define consws (packet_r [4]) // console switch settings
#define joysws (packet_r [5]) // joystick switches
#define rudder (packet_r [6]) // rudder position
#define elevator (packet_r [7]) // elevator position
#define pm_rot (packet_r [8]) // port motor rotate posn
#define sm_rot (packet_r [9]) // stbd motor rotate posn
start_r = 0x00; // clear ENQ
// Transmit Packet to surface
const int max_s = 10; // number of parameters to send
#define ACK 0xaa // Acknowledge
char packet_s [max_s]; // size of send packet array
#define start_s (packet_s [0]) // start byte
#define compass (packet_s [1]) // Compass heading
#define bat_volts (packet_s [2]) // Battery voltage
#define bat_amps (packet_s [3]) // Battery current
#define pitch (packet_s [4]) // pitch angle
#define roll (packet_s [5]) // roll angle
#define status_1 (packet_s [6]) // side thrusters and servos status
#define status_2 (packet_s [7]) // main thruster & PIC status
#define depth (packet_s [8]) // depth from surface
#define dist_from_bottom (packet_s [9]) // distance to bottom
// Initialize transmit packet array
start_s = ACK; // acknowledge in first byte
compass = 0x38; // South direction
bat_volts = 236; // 13.8 volts
bat_amps = 120; // -620 mA
pitch = 0x23; // +35 degrees
roll = 0x85; // -5 degrees
status_1 = 0x00; // clear problems
status_2 = 0x00; // clear problems
depth = 16; // depth
dist_from_bottom = 22; // DTB
// Initialize port directions
set_tris_a(0b00011111); // Port A inputs except E0,E1, E2
set_tris_b(0b11110001); // B1 to B3 outputs, the rest inputs
set_tris_c(0b10011000); // set Port C3, C4, C7 inputs, the rest outputs
set_tris_d(0b11111111); // set Port D Compass & Clinometers to inputs
// Turn off L6201 motor controller
output_low(PIN_C0);
output_low(PIN_C1);
// turn off all controls, lamps & camera
output_low(PIN_B1);
output_low(PIN_B2);
output_low(PIN_B3);
// setup PWM, RS232, and ADC
setup_ccp1(CCP_PWM); // sets up for PWM
setup_timer_2(T2_DIV_BY_16,255,2); // freq of 300 Hz. Note 255 max???
setup_adc (ADC_CLOCK_DIV_32); // configures ADC, 2-6 us reqd in .h
setup_adc_ports (AN0_AN1_AN2_AN3_AN4); // amps, volts, depth, spare, spare
// **********************************************************************************
while (1)
{
// wait for transmission ****************************
timeout_error = FALSE;
while ((start_r != ENQ)&&(!timeout_error)) // wait for enquiry or timeout
packet_r [0] = timed_getc(); // to get first packet
if (! timeout_error)
{
for(i=1;i<max_r;i++) // 10 packets; 0 to 9
packet_r[i] = timed_getc(); // get other 9 packets
}
// prepare reply. Always send the first character received back in status_1 (packet_s[6])
// and if correct increment status_2. If not correct make status_2 all ones (packet_s[7]).
status_1=start_r; // always send first received back
if(start_r==ENQ) // if it is actually 0x55
{
status_2 = status_2 + 1; // increment the counter to send back
}
else
status_2=0xff; // else return all ones
// delay_ms(10); // 80ms is max for 50ms timeout in console, just testing
// send reply
for (i=0; i<max_s; i++) // 7 packets, 0 to 6
putc (packet_s [i]); // send the packet
start_r = 0x00; // clear ENQ
} // end of while loop
} // end of main function
// functions ***********************************************************
// function 'timed_getc' waits up to 50 ms for a character
char timed_getc()
{
long timeout;
timeout_error = FALSE;
timeout = 0;
while (!kbhit() && (++timeout<50000)) // 0.5 sec
delay_us(10);
if (kbhit())
return (getc());
else
{
timeout_error = TRUE;
return(0);
}
}
// end
|
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Sun Apr 30, 2017 10:31 am |
|
|
The reason that ERRORS affects things, is that this copies the UART status into the variable RS232_ERRORS. Since this can't happen till after the byte transfer has completed, adding 'ERRORS' makes the software wait until the transmission has completed. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Apr 30, 2017 11:39 am |
|
|
PCM Programmer;
it is quite simple. The DS3695 has 8 pins, power and ground, two differential line drivers/receivers, an input pin and an output pin.
this leaves the two pins that enable the receiver or driver. one is low select and the other is high select so if they are tied together and connected to the ENABLE pin controlled by #use rs232() the software selects send or receive automatically. I just have to make sure that both chips don't send at the same time, hence the master initiating the communications and always switching immediately to receive (even between sending each bit). I wish I could post a screen shot of the DSO.
I think Ttelmah has the answer but it doesn't explain why the SLAVE does not need ERRORS. I do know that with the full program it does need ERRORS, it is just this test program that works without it. The test MASTER program definitely needs it. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Apr 30, 2017 11:40 am |
|
|
Quote: | How do I send a link to my schematic or pictures? |
Here are instructions on how to link to an image. However, this is not
permanent. The website listed below will delete the image after a while.
How to post an image on the CCS forum:
Go to this website: http://postimage.org/
Upload your image. Select family safe. Then click the first button for
"Hotlink to Forum" to get a link to the image.
Then go to the CCS forum and type Ctrl-V to paste the link into a post.
If postimage.org doesn't work in your country, then use Google to find
another free image hosting site for forums. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Apr 30, 2017 1:07 pm |
|
|
Not quite that easy (for me) I can't find any instructions and nothing is obvious (to me). I think this may work:
Console 1.jpg is the schematic for the console. The PIC on this schematic controls another PIC and several Graphic LCDs by I2C. See the second schematic Console 2.jpg
Enable_send.jpg shows the ENABLE pulse generated by #use rs232(ENABLE=pin_E2)
The last one is Master Enable, Tx, Slave Enable, Rx. Note that the signals seem messy but this is just a function of the sampling of the DSO. If expanded they are just as clear as those in picture 3. I have identified the start bit, the 8 data bits, and the stop bit by expanding and moving the delay just to satisfy myself of what I am seeing.
Last edited by rovtech on Sun Apr 30, 2017 5:22 pm; edited 1 time in total |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Sun Apr 30, 2017 1:21 pm |
|
|
First, you need a pull-up resistor on the RX input to the PIC. When the receive buffer is disabled, this line is floating. It 'may' stay high, but is quite likely not to....
Then you have no sign of any termination or bias on the 485 lines. Termination is needed. Bias is also probably needed (a couple of the drivers 'warrant' to receive 'idle' when the bus is not driven, but most don't). Have a look at:
<http://www.edn.com/design/analog/4442598/Understanding-RS-485-passive-fail-safe-biasing->
If picture 3, is the operation using 'enable', this looks fine. It is disabling the drive as soon as the bus goes idle, so there is a tiny period when it turns off the drive. Provided the bus is biased so it goes to the idle level when this happens, this will give correct data out. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Sun Apr 30, 2017 1:36 pm |
|
|
This is my ROV
photo uploading
and I cannot get the control console pic to show.
thanks Ttelmah. This is going to take me some time reading the data sheets and trying some of your suggestions.
I have removed comments that I added here and put them in a new reply since they really start a new discussion.
Last edited by rovtech on Mon May 01, 2017 8:09 am; edited 1 time in total |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Mon May 01, 2017 8:09 am |
|
|
For anyone following this thread my system is similar to Fig 27, page 19 of http://www.ti.com/lit/an/slla070d/slla070d.pdf, and yes the terminations are a good idea especially if I go to longer cable in the future.
The DS3695 data sheet shows that if ENABLE (/RE+DE) = 0, in other words both in receive, then the bus is high impedance and Rx could be anything. The bus differential voltage for a reliable output is +0.2v or -0.2v. Termination resistors will hold the bus close to 0v. This condition is described in the TI document, referenced above, on page 12 and reproduced below:
The Need for Failsafe Protection
In any party-line interface system with multiple driver/receivers, long periods of time may occur when all
the driving devices are inactive. This state is known as line idle or bus idle and occurs when all drivers
place their outputs into a high-impedance state. During bus idle, the differential bus voltage is left floating
(i.e., indeterminate: neither logic-high nor logic-low state) if there is no termination resistors, and the
differential bus voltage is close to zero in the case where termination resistors are used. In both cases, as
a result, the receiver can be falsely triggered into either a logic-high or logic-low state, depending on the
presence of noise and the last polarity of the floating lines. Obviously, this is undesirable, as the circuitry
following the receiver could interpret this as valid information. It is best to detect such a situation and place
the receiver outputs into a known and predetermined state. The name given to methods that ensure this
condition is receiver failsafe. An additional, desirable feature that a failsafe provides is to protect the
receiver from shorted line conditions, which can again cause erroneous processing of data.
I will add 120 ohm termination resistors at each end and close to the DS3695s. I don't see how a pullup on Rx of the PIC will have any effect since the PIC has active outputs. I'm not sure how the compiler handles the input if it is flipping randomly but it does not do this and it works reliably with no termination resistors as long as ERRORS in in the #use rs232().
I'm not sure how to make this "both listening" condition failsafe. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Mon May 01, 2017 8:15 am |
|
|
and this "Rx could be anything", is why there needs to be a pull-up resistor on the line to the PIC. |
|
|
rovtech
Joined: 24 Sep 2006 Posts: 262
|
|
Posted: Mon May 01, 2017 8:58 am |
|
|
I don't think so. Rx is determined by the state of the bus and the DS3695 will send Rx high or low depending on noise on the bus, or the last state of the bus which I think is the case. The only "pullup" that can help is on the bus to provide a bias in one direction or the other. Putting a pullup on an active output will have little, if any, effect.
I meant the DS3695 has active outputs, not the PIC, in the above. Tx is an input. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19255
|
|
Posted: Mon May 01, 2017 9:14 am |
|
|
When the RX buffer is disabled, (/RE is high) 'RX can be anything'.
When RE is 1, RO is 'X' (high impedance). At this point, the receive input to the PIC is _floating_. It's 'probably' stay high(ish), but any RF noise, or resistance on the board, and it'll drift low. Result 'garbage' received..... |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
|
|
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
|