|
|
View previous topic :: View next topic |
Author |
Message |
beeson76
Joined: 31 Jul 2012 Posts: 8
|
Need help with this program |
Posted: Thu Aug 02, 2012 10:49 am |
|
|
I know this is a long shot:)
I have a TPIC6595 8 Bit Shift Register on a Board that is "connected" to a PIC16F628A Microchip.
Here are the connections from the Pic to the Shift Register
RA1 goes to RCK
RA0 goes to SRCLR
RA2 goes to SRCK
RA3 goes to G
RA4 goes to SER IN
And then the the 8 Drain from the Shift Register are all connected to corresponding relays which in turn are connected to motors.
You wont like it very much but I don't have a schematic of the circuit but I can certainly provide pictures, and maybe draw up a crude schematic in autocad or something.
As far as the inputs I have 7 inputs (PORTB) on the Microchip that is connected to a controller that eventually controls the motors. For example, you hit Switch1, and the microchip recognizes the button press and sends a control to the shift register which in turns sends control to the relay for Motor1. (I am assuming that is how is works, because I don't really know anything about shift registers)
So now to the problem. The code I have, which is provided is locking up my Microchip. I am assuming it is the code anyway. After a while the microchip locks up and the motors stay running. The only way of stopping it, is to power down the microchip and listen for the relays to unlatch.
Can anyone check the code, ecspecially the make_it_so function and see if you think there is a conflict in this function. I did not write the code, just inherited it:) I believe this is the code that takes the button press and sends it to the shift register, which in turns sends it to the relays.
I know just enough programming to be dangerous with it, and I really know nothing of the CCS compiler though I am learning.
Any help in getting pointed in the right direction would be greatly appreciated
Code: |
/*******************************************************************************
--------------------------------------------------------------------------------
Filename: pcs-rl.c
Function:
Motor control using PIC16F628 micro-controller.
Internal oscillator at 4MHz. Output to motors using 8 bit shift register.
By: Don Rivera (PDDC)
Versions: Release 01/30/04
5/12/04 use fast_io, device 628a
--------------------------------------------------------------------------------
*******************************************************************************/
/* Pre-processor **************************************************************/
#include "16F628.h" // Header file.
#include <stdlib.h>
#case // Makes CCS case sensative.
#use standard_io(A)
#define ON 1 // These are useful.
#define OFF 0
#define bool short int // 'Boolean' and "short int" take too long to type!
#define SRCLR PIN_A0 // Clears input shift register (active low).
#define RCK PIN_A1 // Sends shift register data to outputs of device.
#define SRCK PIN_A2 // Shift register clock (advances serial data on L-to-H).
#define G PIN_A3 // Enable outputs of shift register.
#define SIN PIN_A4 // Serial data input (I/O must have a pull-up resistor).
/* Global Variables and Setup**************************************************/
struct pendant_map { // Defines the pendant port.
bool col_1; // Up
bool col_2; // Down
bool unused_col_3;
int keys : 5; // Head, Foot, Bed, Seat, Trend, Chair.
}pendant;
#byte pendant = 6 // To port B.
struct function { // Maps the function to a port pin.
bool hu;
bool hd;
bool fu;
bool fd;
bool bu;
bool bd;
bool t;
bool tr;
bool su;
bool sd;
};
struct scan { // Holds the information grabbed from the port.
int in : 5;
int unused : 3;
};
struct key { // Used to decode the individual port pins.
bool h;
bool f;
bool b;
bool t;
bool s;
bool un1;
bool un2;
bool un3;
};
union combine { // See above 2 blocks.
struct scan a;
struct key b;
};
union mix{ // Used to convert up/down to one variable.
struct function data;
long ldata;
};
int const PRESCALE = 13; // Timer0 preset value. For perfect 4MHz (maybe).
int go_thous = 0; // For main process loop entry.
int to_out = 0; // Data that is sent to 6595 IC.
struct scan up; // Holds pendant up data 'up.in'.
struct scan dn; // Holds pendant dn data 'dn.in'.
union mix info; // This is 2 pendant data values converted to 1 long.
long deb_info; // Use as temp storage to debounce buttons in 'isr'.
union mix use_info; // Actual modified pendant data to use.
/* Function declarations ******************************************************/
void setup_io(void); // Sets up all I/O ports.
void start_rtcc(void); // Sets up the rtcc counter.
void convert(void); // Convert 2 pendant data values to 1 long.
void check_input(void); // Enforces buttons pressed follow rules.
void motor_timers(void); // Sets "change of direction" timers.
void process_state(void); // Determines outputs for running motors.
void rtcc_isr(void); // Interrupt service routine.
void make_it_so(void); // Output to motors.
// Main ***********************************************************************
main() {
setup_io();
start_rtcc();
while(TRUE) {
if(go_thous){ // This variable set in isr.
go_thous = 0;
check_input();
motor_timers();
process_state();
make_it_so();
}
}
return 0;
}
// Function definitions ********************************************************
void setup_io(void) {
set_tris_b(248); // 1-input, 0-output. All out.
port_b_pullups(TRUE); // Use internal pullups.
output_high(G); // Turns off outputs.
output_low(SRCLR); // Clear input register.
output_high(SRCLR); // Input register back on.
output_low(SRCK); // Set clock input low.
output_low(RCK); // Set sr clock low.
output_low(SIN);
}
void start_rtcc(void) {
setup_counters(RTCC_INTERNAL, RTCC_DIV_4);
set_rtcc(PRESCALE); // Tunes the actual time to overflow.
enable_interrupts(INT_RTCC);
enable_interrupts(GLOBAL);
}
#int_rtcc // RTCC interrupt subroutine.
rtcc_isr(void) {
static int debounce = 0; // Debouncing flag.
int const DEBOUNCE_TIME = 5; // Amount X state_mach(max n+1) = milliseconds.
static int debounce_ctr = 0; // Duh!
static int state_mach = 0; // Usefull for switch/case block.
set_rtcc(PRESCALE); // Restart with prescale value (~ 1mS).
go_thous = 1; // For section of "main loop" entry.
switch(state_mach) {
case 0:
pendant.col_1 = 0; // Set up column low to check pendant.
state_mach = 1;
break;
case 1:
up.in = ~pendant.keys; // Pullups used so ~port to get a high value
state_mach = 2; // on the button pressed.
break;
case 2:
pendant.col_1 = 1; // Change to scan down column.
pendant.col_2 = 0;
state_mach = 3;
break;
case 3:
dn.in = ~pendant.keys; // Same as above.
state_mach = 4;
break;
case 4:
pendant.col_2 = 1; // Restore down column to 'high'.
state_mach = 5;
convert(); // Convert the data to a single long.
break;
case 5:
if(debounce){
if(--debounce_ctr == 0){ // Only do work if debounce completed.
debounce = OFF;
if(info.ldata == deb_info) { // If button still pressed.
use_info.ldata = deb_info; // Keep good data.
}
}
}
else {
// Or if key changed.
if(info.ldata != use_info.ldata){
deb_info = info.ldata; // Save, to test it later.
debounce_ctr = DEBOUNCE_TIME; // Reset debounce timer.
debounce = ON; // Start the timer.
}
}
state_mach = 0;
break;
default:
state_mach = 0;
break;
}
return 0;
}
void convert() {
union combine work; // Used to decode functions in the input data.
info.ldata = 0; // Start with no data in the holder.
work.a.in = up.in; // Put in up data.
if(work.b.h)
info.data.hu = ON;
if(work.b.f)
info.data.fu = ON;
if(work.b.b)
info.data.bu = ON;
if(work.b.t)
info.data.t = ON;
if(work.b.s)
info.data.su = ON;
work.a.in = dn.in; // Repeat above for down side.
if(work.b.h)
info.data.hd = ON;
if(work.b.f)
info.data.fd = ON;
if(work.b.b)
info.data.bd = ON;
if(work.b.t)
info.data.tr = ON;
if(work.b.s)
info.data.sd = ON;
}
void check_input(void) {
if(use_info.data.hu && use_info.data.hd) // Up/dn can never run together.
use_info.data.hu = 0;
if(use_info.data.fu && use_info.data.fd)
use_info.data.fu = 0;
if(use_info.data.bu && use_info.data.bd)
use_info.data.bu = 0;
if(use_info.data.t && use_info.data.tr)
use_info.data.tr = 0;
if(use_info.data.su && use_info.data.sd)
use_info.data.su = 0;
// Place these in the order of precedence. Trend is master.
if(use_info.data.t) {
use_info.ldata = 0;
use_info.data.t = ON;
}
if(use_info.data.tr) {
use_info.ldata = 0;
use_info.data.tr = ON;
}
if(use_info.data.sd) {
use_info.ldata = 0;
use_info.data.sd = ON;
}
if(use_info.data.su) {
use_info.ldata = 0;
use_info.data.su = ON;
}
}
void motor_timers(void) {
static struct function delays; // Delay flags.
static long hd_ctr = 0; // Counters for up/dn delays.
static long hu_ctr = 0;
static long fd_ctr = 0;
static long fu_ctr = 0;
static long bd_ctr = 0;
static long bu_ctr = 0;
static long t_ctr = 0;
static long tr_ctr = 0;
static long su_ctr = 0;
static long sd_ctr = 0;
static union mix last; // Save to compare to button for timers.
// Start change of direction delay timers if needed.
// If dn is off but was on, start the delay for up. etc...
if(!use_info.data.bd && last.data.bd) {
delays.bd = ON;
bd_ctr = 750;
}
if(!use_info.data.bu && last.data.bu) {
delays.bu = ON;
bu_ctr = 750;
}
if(!use_info.data.fd && last.data.fd) {
delays.fd = ON;
fd_ctr = 750;
}
if(!use_info.data.fu && last.data.fu) {
delays.fu = ON;
fu_ctr = 750;
}
if(!use_info.data.hd && last.data.hd) {
delays.hd = ON;
hd_ctr = 750;
}
if(!use_info.data.hu && last.data.hu) {
delays.hu = ON;
hu_ctr = 750;
}
if(!use_info.data.t && last.data.t) {
delays.t = ON;
t_ctr = 750;
}
if(!use_info.data.tr && last.data.tr) {
delays.tr = ON;
tr_ctr = 750;
}
if(!use_info.data.su && last.data.su) {
delays.su = ON;
su_ctr = 750;
}
if(!use_info.data.sd && last.data.sd) {
delays.sd = ON;
sd_ctr = 750;
}
// This section prevents a direction change on the motors too quickly.
// Count down until opposite function can be used.
if(delays.fd) {
if(--fd_ctr == 0)
delays.fd = OFF;
else {
use_info.data.fu = OFF;
use_info.data.su = OFF;
}
}
if(delays.fu) {
if(--fu_ctr == 0)
delays.fu = OFF;
else {
use_info.data.fd = OFF;
use_info.data.sd = OFF;
}
}
if(delays.hd) {
if(--hd_ctr == 0)
delays.hd = OFF;
else {
use_info.data.hu = OFF;
use_info.data.su = OFF;
}
}
if(delays.hu) {
if(--hu_ctr == 0)
delays.hu = OFF;
else {
use_info.data.hd = OFF;
use_info.data.sd = OFF;
}
}
if(delays.bd) {
if(--bd_ctr == 0)
delays.bd = OFF;
else {
use_info.data.bu = OFF;
use_info.data.t = OFF;
use_info.data.tr = OFF;
use_info.data.su = OFF;
use_info.data.sd = OFF;
}
}
if(delays.bu) {
if(--bu_ctr == 0)
delays.bu = OFF;
else {
use_info.data.bd = OFF;
use_info.data.t = OFF;
use_info.data.tr = OFF;
use_info.data.su = OFF;
use_info.data.sd = OFF;
}
}
if(delays.t) {
if(--t_ctr == 0)
delays.t = OFF;
else {
use_info.data.tr = OFF;
use_info.data.bd = OFF;
use_info.data.bu = OFF;
use_info.data.su = OFF;
use_info.data.sd = OFF;
}
}
if(delays.tr) {
if(--tr_ctr == 0)
delays.tr = OFF;
else {
use_info.data.t = OFF;
use_info.data.bd = OFF;
use_info.data.bu = OFF;
use_info.data.sd = OFF;
}
}
if(delays.su) {
if(--su_ctr == 0)
delays.su = OFF;
else {
use_info.data.hd = OFF;
use_info.data.fd = OFF;
use_info.data.sd = OFF;
use_info.data.t = OFF;
use_info.data.bu = OFF;
use_info.data.bd = OFF;
}
}
if(delays.sd) {
if(--sd_ctr == 0)
delays.sd = OFF;
else {
use_info.data.hu = OFF;
use_info.data.fu = OFF;
use_info.data.su = OFF;
use_info.data.bu = OFF;
use_info.data.t = OFF;
use_info.data.tr = OFF;
}
}
last.ldata = use_info.ldata;
}
void process_state(void) {
struct bits{ // This is how the port is laid out.
bool fbu;
bool fbd;
bool hbu;
bool hbd;
bool fd;
bool fu;
bool hd;
bool hu;
};
union both{ // So the port doesn't need to be accessed pin by pin.
int output;
struct bits out;
}process;
process.output = 0;
if(use_info.data.hu) {
process.out.hu = ON;
}
if(use_info.data.hd) {
process.out.hd = ON;
}
if(use_info.data.fu) {
process.out.fu = ON;
}
if(use_info.data.fd) {
process.out.fd = ON;
}
if(use_info.data.bu) {
process.out.fbu = ON;
process.out.hbu = ON;
}
if(use_info.data.bd) {
process.out.fbd = ON;
process.out.hbd = ON;
}
if(use_info.data.t) {
process.out.fbu = ON;
process.out.hbd = ON;
}
if(use_info.data.tr) {
process.out.fbd = ON;
process.out.hbu = ON;
}
if(use_info.data.su) {
process.out.hu = ON;
process.out.fu = ON;
process.out.fbd = ON;
process.out.hbu = ON;
}
if(use_info.data.sd) {
process.out.hd = ON;
process.out.fd = ON;
process.out.fbd = ON;
process.out.hbd = ON;
}
to_out = process.output;
}
void make_it_so() {
static int last_to_out = 0;
int ctr;
if(to_out != last_to_out) {
output_high(G); // Turns off outputs.
output_low(SRCLR); // Clear input register.
output_high(SRCLR); // Input register back on.
// Shift data out to shift register.
for(ctr = 0; ctr < 8; ctr++) {
output_bit(SIN, bit_test(to_out, ctr));
output_high(SRCK); // Data shifts on L-to-H.
output_low(SRCK);
}
output_high(RCK); // Send data to outputs of shift register.
output_low(RCK);
output_low(G); // Enable shift register outputs.
last_to_out = to_out; // Save the most recent change.
}
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Aug 02, 2012 12:30 pm |
|
|
Quote: | After a while the microchip locks up and the motors stay running. |
Quite often this type of problem is caused by electrical noise put onto the
power and/or ground rails by the relays and motors. A quick way to
check this is to disconnect all your motors and relays, and perhaps
substitute low current LEDs (5ma) in their place. Then run your program
and see if it still locks up. |
|
|
ezflyr
Joined: 25 Oct 2010 Posts: 1019 Location: Tewksbury, MA
|
|
Posted: Thu Aug 02, 2012 12:37 pm |
|
|
Hi,
You are asking a lot to essentially dump a bunch of code on the forum, and then say "why doesn't it work?" That's not really how the forum is supposed to work, ie., we aren't supposed to be doing your job!
Having said that, has this project ever worked? At what stage of the game did you "inherit" it? Do you know if the hardware has been fully debugged, and that this is strictly a software problem, or you aren't sure?
A "shift register" is simply a hardware device that takes a serial bit stream as an input, and then translates the stream of bits into a "parallel" word. The fact that your shift register outputs are controlling relays which are in turn controlling motors raises all kinds of red flags. That is not an inherently undesirable configuration, but you must take certain steps to ensure that inductive "kicks" generated by the relay coils, and EMI/RFI noise generated by the motors does not cause a latch-up problem with the PIC. At a minimum you need a diode across each relay coil, and a capacitor across each motor.
It's possible that your code IS the problem, but until you totally rule out hardware issues, I wouldn't spend a lot of time on that code. Instead, I would reduce that code by 90%, and just write something that repeatedly turned On and Off different motors to verify the hardware. It looks like all the code is there to drive the shift register, so you just need to have a small program that steps thru the possible outputs, and calls the output routine.
John |
|
|
beeson76
Joined: 31 Jul 2012 Posts: 8
|
|
Posted: Thu Aug 02, 2012 3:48 pm |
|
|
Thanks PCM Programmer and Ezflyer for the advice.
I following your advice and its working great now. So it appears that the code is fine, and it is on the Hardware side latching the relays. It took a little while for me to make my test board, and its turning out like you guys thought...hardware and EMI, or other interference.
Thanks again for the help. I certainly do appreciate it very much. Thanks again:) |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9241 Location: Greensville,Ontario
|
|
Posted: Thu Aug 02, 2012 3:51 pm |
|
|
query..
Do you have diodes across the all relay coils ? Without them, you'll get a lot of 'spikes', EMI, and other 'nasties' that can easily crash a PIC program. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Thu Aug 02, 2012 6:32 pm |
|
|
* return 0 in main is worthless and does not execute.
* CCS tends to prefer int1 instead of bool, which gives the clear impression of rewarmed code , written for another compiler.
* no #fuses ?
* you declare standard_io - then go ahead and do a set_tris ??
is this vestigial code from another compiler ? RTM <<
*at a glance I think a lot of the 1bit structure code could be done without so many If/Thens. Since you are not inverting most of the bit tests -
BIT MASKING would seem a simpler and faster solution.
Convert, check_input, motor_timers, and process_state all seem like they could be done better with masking.
Also masking guarantees that if you are wanting to synchronously change all the bits on, say portB, that you can mask a "port shadow" variable and then do a single output_B(shadowvar);
When you structure and "unionize" physical ports, the series of if-thens
you coded will change the bits one at a time - and in the sequence of the tests - NOT as a single group on a single instruction. Perhaps thats what you need, but the code is far from clear.
* if to_out =0 on the first call to make it so - it will not be sent.
Are you sure that to_out will NOT be zero, ever - especially on first make it so ?? There is a safer way to optimize that routine if zero is an output option, on first run. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19537
|
|
Posted: Fri Aug 03, 2012 8:43 am |
|
|
Seriously, I'd be looking at spikes. The bit that leaps out to me, is the comment about 'motors'. Thing you have to remember, is that energy always has to go 'somewhere. The TPIC6595, has it's own spike suppression, but this requires a very good ground at this chip. This though will only trap the energy on the relay coils. What happens when the motors are switched off?. You probably get both inductive spiking from the motor coils, and also overrun generation from the motors themselves. Both have to be controlled. Even if only with little spark suppression capacitors across the relay contacts. What you are trying to do shrieks 'noise'.....
Best Wishes |
|
|
beeson76
Joined: 31 Jul 2012 Posts: 8
|
|
Posted: Fri Aug 03, 2012 12:27 pm |
|
|
I have a circuit board layout, but I cant post it here because I don't know how.
I do have 1N4004 diodes across the coil. I guess does it matter which direction the diodes are facing. Sorry for the basic question, but Im learning:)
And it was mentioned that I needed good grounding at the TPIC6595. What does that mean:) I have it grounded to a copper grounding block in the board...but the grounding block doesnt have a direct connection to the ground wire (green wire) coming onto the board from the AC. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Aug 03, 2012 12:32 pm |
|
|
Quote: |
do have 1N4004 diodes across the coil.
|
if they were WRONG - you would know already or be blowing fuses or drivers
|
|
|
beeson76
Joined: 31 Jul 2012 Posts: 8
|
|
Posted: Fri Aug 03, 2012 12:38 pm |
|
|
Thanks asmboy:)
I havent been blowing anything up:)....yet anyway.
I wish I could post that circuit layout...it would be easier to explain.
One side of the coil is coming from the 12 volt voltage regulator, and the other side is coming from the TPIC. So I guess the TPIC is acting as the common or ground. I have the anode side (side without mark) connected to the TPIC line and the cathode side (side with mark/line) connected to the 12 volt side. |
|
|
asmboy
Joined: 20 Nov 2007 Posts: 2128 Location: albany ny
|
|
Posted: Fri Aug 03, 2012 12:42 pm |
|
|
The combination of static current - and coil inductance determines circuit performance.
Sometimes ( with very high L coils) being able to insert a SMALL resistance ( less than a 1/10 th of coil DC-R) in a between the coil and driver output also helps damp noise spikes too. (dampens the charging time - hence 'Q" of the coil ) |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Simplify simplify simplify |
Posted: Fri Aug 03, 2012 2:18 pm |
|
|
Break your problem down into manageable chunks.
There are always two aspects to any microcontroller problem, hardware & software.
You need to solve them one at a time.
You've been given loads of advice but don't seem to be a lot nearer to a solution.
I suggest you write simple, short code which turns motors/relays on/off at regular intervals. Get this code to work first, either via the serial interface or not, then progress. Use a 'scope to investigate what's going on.
You can show us a simplified version of your circuit with ASCII artwork.
Mike |
|
|
|
|
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
|