PDA

View Full Version : Cruise Control



Picstar
- 6th March 2012, 05:48
Hello All,
For those that are interested, I'm reporting my progress on a homemade Cruise for cars and motorcycles. After mounting a stepper motor and linking it mechanically to my throttle, I tapped into my vehicles VSS, (Vehicle Speed Sensor). I also tapped into the brake light switch which provides an emergency shut-off for the unit, as well as having a good ol' on/off switch mounted inside.
The theory of operation is fairly simple, the unit counts pulses continuously from the VSS. When a momentary switch is pushed it moves on a gets a second count from the VSS and from that point on it just continues to read pulses, comparing it to the first reading, and make adjustments to the throttle. People probably wonder why I don't just but an aftermarket cruise unit but I was just interested in creating a true feedback system that I could experiment with.
Well after playing with some of the timing elements in the program, I got the unit to work but, with a few problems. The first problem is that it is slow to respond to hills and overcompensates trying to climb them, often engaging passing gear. On flat open roads its performance is very good.
Another problem is that it apparently cannot read the VSS pulses past about 45 mph. I've tried increasing and decreasing the timing in my COUNT statements, but to no avail. I have also tried the divide reading by 2 and /10 method, but still no luck. It may be necessary to mount a magnetic pick-up unit to the axle or CV joint. I am just an amateur programmer with no knowledge of assembly, C or other languages, but I need a system that continuously reads the pulses, without the delay of a count statement, and continuously makes adjustments. I am not selfish with my code, (I'm not going to sell these units), so if you have an old vehicle to play with you can play with my code. Be warned that safety is the number one factor and any tests should be far from city traffic. Also, I am not responsible for any mishaps, THIS IS ALL JUST TEST CODE, USE AT YOUR OWN RISK. For those that wish to weigh in on better code, your input will be greatly valued. Thanks in advance.

' Cruise Control Program
' Microcontroller used: 16F88
' 20 MHZ CRYSTAL fuse at hs
' Porta.0 - Porta.3 are stepper motor outputs
' Portb.0 is signal in from VSS (Vehicle Speed Sensor)
' Portb.1 is the signal to engage when 5v is applied. 10K tied to ground
' Portb.2 is tied to the brake switch. 10 k tied to ground
' Fine tuning may needed to the "ti" and "ts" variables for vehicle,
' ts may need to be a "byte" instead of "word". Depending on pulses per second


START:
define osc 20
clear
C1 VAR word
I VAR WORD
ti var byte
ts var word
B1 var word
B2 VAR word
TRISa = %00000000
trisb = %00000111
portb = 0
porta = 0
C1 = 5000 ' var. controls pause if b2=b1 (0 - 65000)
ti = 200 ' stepper motor speed (20 - 200)
ts = 5000 ' this changes how long of a count in milliseconds (100-2500)

INIT:
IF PORTb.1 = 1 THEN COUNTER1 'Press sw1 to engage
pause 100
GOTO INIT


COUNTER1:
IF TS < 1000 THEN TS = 1000
count portb.0 ,ts, B1 ' read pulses from vss for ? second
PORTB.3 = 1
pause 100


CRUISE:
count portb.0 ,ts, B2 ' get a second reading and store in B2
PORTB.4 = 1
IF PORTb.2 = 1 THEN porta = 0:goto START ' BRAKE LIGHT SIGNAL, kill NOW
IF B2 < B1 - 3 THEN FORW
IF B2 > B1 + 3 THEN REVR


Lock:
FOR I = 1 TO c1
IF PORTb.2 = 1 THEN porta = 0:goto START
NEXT I
goto cruise


FORW:
PORTa = %00000011
PAUSe ti
PORTa = %00000110
PAUSE ti
PORTa = %00001100
PAUSE ti
PORTa = %00001001
PAUSE ti
GOTO cruise


REVR:
PORTa = %00001001
PAUSE ti
PORTa = %00001100
PAUSE ti
PORTa = %00000110
PAUSE ti
PORTa = %00000011
PAUSE ti
GOTO cruise

Art
- 6th March 2012, 14:53
You can't beat an interrupt driven speed calculation routine.
Then that part your program won't be held up by any PBP command at all.

Picstar
- 6th March 2012, 19:11
Yes Art you keep taunting me about interrupts and how great they are, I agree. Well I've shown you my code, why don't you help a guy out instead of sniveling, and add to it.

ScaleRobotics
- 6th March 2012, 23:46
Yes Art you keep taunting me about interrupts and how great they are, I agree. Well I've shown you my code, why don't you help a guy out instead of sniveling, and add to it.

Uh ... surely you are joking .... right? Because I heard no sniveling.

Lot's of people are here to help you. But very few are here to do all your work for you ...

Art
- 7th March 2012, 01:08
What you want is not much different from the interrupt code I posted in your other thread.
You just need to add a line to count the interrupt events.

Regardless, I would begin with DTs Elapsed Time code,
and add the portb.0 interrupt from there.

If you just get the elapsed timer working,
so you can check within any program that the SecondsChanged
variable is set (a second has ticked over),
it already contains an interrupt routine for a timer.
Then trust me, I'm in a good position to help you.

You don't add to what you've got,
best put it aside and use parts of it that do what you want,
but consider beginning with the Elapsed timer demo and adding what you need to that.

Picstar
- 7th March 2012, 03:58
Well S.C. , I've done my own work, but there are several concepts that are a little beyond my grasp, although I'm trying. I am 67 years old and its a little bit hard to understand when someone says, " Just cut and paste this here, and write an assembler routine and add it here....". I have bought books like, "Revolutionary Assembly Language" and "Learning Assembly Language the Easy Way", all to no avail. I guess its like a cave man trying to understand a computer. At some point in our lives things just don't sink in that well. But I do appreciate Art's input but its like asking your mechanic what is wrong with your car and he replies "read the book".
Maybe I should just quit, throw away the development board and PBP and just watch tv all the time, would that be better?

HenrikOlsson
- 7th March 2012, 06:24
Hi,
Another option (still involving interrupts though) is to feed the pulses to the input of TMR1 and set that up as a counter. That way the the pulses are counted in hardware and you can just "collect" the count at a suitable interval - which is where the interrupt comes in (or you could use PAUSE if you don't need to do anything else at the same time).

TMR1H = 0 'Clear count
TMR1L = 0
T1CON.1 = 1 'Set TMR1 as counter, clock on T1CKI

Now the TMR1 will count the pulses on the T1CKI-pin and you can read it like

PulseCount VAR WORD
PulseCount.HighWord = TMR1H
PulseCount.LowWord = TMR1L

If you don't want to reset the TRM1 count each time you can just take the difference between 'this' reading and the 'last' reading

oldCount VAR WORD
oldCount = PulseCount
PulseCount.HighWord = TMR1H
PulseCount.LowWord = TMR1L
PulseCount = PulseCount - oldCount

If you call this code at a specified interval (by timer interrupt or a simple PAUSE) you'll always have the current 'speed' in the variable PulseCount. This could be done with ON INTERRUPT but DT-INTS would be 'better'. We tend to say it's easy, which it is once you know how to do it but it can be intimidating at first. However there are many examples for DT-INTS and timer interrupts on the forum. If you can't find it and/or you need help that's what we're here for but give it a try first - if this is a suitable approach to your project of course.

/Henrik.

Picstar
- 7th March 2012, 07:12
Thanks Henrik,
Now I can understand this code. Using the TMR1 is exactly what I need to do. I notice that you use both a Highword and a low word, I assume this detects the rising and falling edges of the signal. But I'm still a little confused, do I add the high and low counts together? AAAAgggg.... I'm thinking of getting Flowcode 5 and throwing PBP away. Any thoughts?

HenrikOlsson
- 7th March 2012, 07:34
Hi,
No, TMR1 is 16bits wide so it consists of two bytes, TMR1H and TMR1L, meaning it can count from 0 to 65535 pulses before it rolls over. These two bytes gets "put together" into the WORD size variable called PulseCount.

/Henrik.

EDIT: Get Flowcode, perhaps.... Throw away PBP, not a chanse! Learning curve, yes of course but you'll get the hang of it. I bet Flowcode comes with a learning curve as well.

Picstar
- 7th March 2012, 07:50
O.K. but where is the interrupt in your program? I've had PBP for 5 or 6 years now and there are somethings I just don't get and I guess never will, plus people are "tight" with their code, I guess they think you might make money off of it, heaven forbid. But with Flowcode 5 they give 50 free hours of online courses, they have a forum where you can chat with the actual designers of the software and there are numerous examples covering just about every subject in-depth.

HenrikOlsson
- 7th March 2012, 08:47
Hi,
There is no interrupt in my program, since using DT-Ints have been covered many many times before on this forum and is pretty well documented on Darrels web-site (http://darreltaylor.com/DT_INTS-14/intro.html) I thought you might have a go at it yourself. I don't think it's matter of being tight with code it's just that each and every application is a little different so it's not just a matter of here you go, use this it'll work exactly the way YOU want.

I have no way of testing this here so this may actually be more of a problem than help but since you seem reluctant to try yourself here goes nothing.
First you need to download the files from Darrels web-site and place them in the PBP directory (or in the project directory). Then

INCLUDE "DT_INTS-14.bas"
INCLUDE "ReEnterPBP.bas"

Then you need to configure the interrupt source and tell the system which subroutine to call when the interrupt occurs

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR0_INT, _UpdateCount, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM
Here we tell the system that we want to trip an interrupt when TMR0 overflows. When that happens we want the UpdateCount subroutine to be called. That subroutine is to be written in PBP and we want the system to automatically clear the interrupt flag for us.


Now you need to actually set up TMR0 so it interrupts at a suitable interval. At 20MHz it ticks a long a 5Mhz meaning , since it's 8bits wide, it'll overflow every (1/5000000)*256 = 51.2us. That'll be a little fast for you I think but luckily TMR0 has prescaler which you can use if disable the Watchdog timer. With the prescaler set to 1:256 you'll get an interrupt rate of (1/5000000)*256*256=13.11072ms or ~76Hz.

TMR0 is controlled thru OPTION_REG so

OPTION_REG.5 = 0
OPTION_REG.4 = 0
OPTION_REG.3 = 0
OPTION_REG.2 = 1
OPTION_REG.1 = 1
OPTION_REG.0 = 1
This should set TMR0 up in "free running mode" with a prescaler of 1:256 meaning it'll overflow every 6.5536ms.

Then you set up TMR1 as a counter as shown before.

Then you enable the interrupt

@ INT_ENABLE TMR0_INT

Now, every time the TMR0 overflow it'll call the Interrupt Service Routine called UpdateCount

UpdateCount:
Tick VAR BYTE
PulseCount VAR WORD
oldCount VAR WORD

Tick = Tick + 1
If Tick = 76 THEN ' Count 76 interrupts to get roughly one second
oldCount = PulseCount ' Previous count
PulseCount.HighByte = TMR1H
PulseCount.LowByte = TMR1L
PulseCount = PulseCount - OldCount 'Take difference between this and the previous difference.
Tick = 0
ENDIF

@ INT_RETURN ;Return from ISR


Now, PulseCount will be updated with the current speed automatically, in the background, once per second.

Don't expect this to just work. I have no way of testing it here so use it as a starting point.
Read the datasheet and compare the settings, does it match, does it add up? Read Darrels website. Do you understand how it works.

I wouldn't expect to buy Flowcode and NOT having to read any documentaion. There ARE numerous example (here on the forum), there ARE a lot of people here to help, Darrel (who now works for MELABS) is here and if that's not enough you can always visit the forum at MELABS website where the other guys from MELABS answers questions too. I wouldn't expect anyone to write the application for you though.

/Henrik.

Picstar
- 7th March 2012, 08:59
Thanks for all of your help Henrik but it will take me awhile to digest all of this, maybe forever. Well, I seem to be going in circles, I think I'll go to bed and move on to another project tomorrow, because I just don't get it.

HenrikOlsson
- 7th March 2012, 10:53
What exactly is it you don't "get" ?
Is it how TMR1 works a a counter? Is how TMR0 works a timer? Is it how the prescaler works? Is it how DT-INTS works? Take one part at a time, try to understand it, ask specific questions as to what you don't understand and I'm sure people will try to help and explain, I know I will, but please keep it specific otherwise it just gets overwhelming as apparently my last post was.

/Henrik.

Art
- 7th March 2012, 14:57
"Maybe I should just quit, throw away the development board and PBP and just watch tv all the time, would that be better?"

no, & I'm not sure what the payoff is for you in saying that.

"I'm thinking of getting Flowcode 5 and throwing PBP away. Any thoughts?"

PBP is not the limitation, throw it away (for a particular program) when PBP has become the limitation.

Henrick is looking at a different approach, so don't confuse the two.

You understand procedure, but are not using picbasic like a real language.
Some of the conveniences provided by PBP make it too easy.
You are closer than you think to having your eyes opened.

ScaleRobotics
- 10th March 2012, 15:19
Bruce has a nice pulse measurement example using CCP1 interrupts. This might give you a head start, although it was written for a PIC18F242. It is well commented. This could replace the pulsin part of your code, so that your speed detection is much more accurate. http://www.picbasic.co.uk/forum/content.php?r=479-Pulse-Measurement-using-CCP1-and-DT-Interrupts (the second code example uses Darrel Taylor's Interrupts.)

chips123
- 13th March 2012, 03:44
I am pretty much a rookie. I have to second the CCP (runs in the background) method. I was doing a speedometer project a while back and started with count, then pulsein, then looked at interrupts. Instead, I went with CCP with excellent results. Accurate and no missing pulses...and a bit easier concept (for me at the time) than trying to grasp the mystical world of interrupts. Start your timer, check to see if the CCP flag is set, and if so, look at the CCP register to get the resulting time elapsed. If the time is long and the timer overflows, you have to account for that. You may want to add an averaging or filter routine once you get an accurate speed measurement. Programming is not that hard if you do it in small bits(haha), test them and go on to the next portion of code for your project. It is time consuming and frustrating until you get it to work, it then seems simple and clear and rewarding. I am not famialir with flowcode, but PicBasic is an excellent alternative to learning assembly or C. It gives you plenty of control and access to the micro while keeping the compiled code compact.

Picstar
- 13th March 2012, 11:15
I appreciate your insight chips123. Yes, I am still studying CCP and have found a few examples on the net. It is a puzzling project, more than I thought at first, because a "real" cruise control system does things like: slowing down before cresting a hill, so as not to overspeed, also it "smooths" out acceleration and if possible finds a spot that it locks at on straight roads. I am still trying to figure out whether I need to use just one CCP module or do I need two of them. Anyhow any chance of your showing some code of your experiments? Thanks

chips123
- 13th March 2012, 12:50
I would think you would only need one ccp. You are just trying to monitor the one thing, vehicle speed. The smoothing out part is a whole other portion of code, I presume. That is where the averaging/filtering will come in. I will look for that code tonight, as I should go to work now.

chips123
- 13th March 2012, 23:04
Below is a snippet. I did this a long time ago, so it is not that fresh in my mind.

Remember that it does not give you the speed or the time between pulses directly. It provides the number of "counts" or "clicks" that have taken place and each count represents x microseconds which depends on clock speed, ccp setup and so on.

The PIR1.5 may not be the correct for your specific chip.

Hope this give you some direction.





'configure your PIR, PIE and CCP registers, etc as needed for your specific chip.

start: 'do something here like display time or speed


waithigh: 'wait here for rising edge and
'handle timer overflow as needed


if PIR1.5 = 0 then waithigh 'wait for rising edge
TMR1L = 0 'start timer over
TMR1H = 0
PIR1.5 = 0 'clear ccp flag

clicks.byte0 = CCPR1L 'value of ccp to clicks variable
clicks.Byte1 = CCPR1H

'do something here like calculate time
'do something here like calculate speed

goto start

chips123
- 14th March 2012, 00:12
I can't figure out how to edit my post. Correction: "I hope this gives you some direction."

Plcguy
- 21st March 2012, 16:52
I'm not sure if your keeping up with the VSS count, but if you start missing pulses, the aliaing will kill your control. I would build a test circuit with an LCD and make sure that the VSS count works to 100mph.
Then I think you need a PID or similar. What you appear to be doing is if the error increases over a fixed time, your output increases in a fixed manner. I would expect it to lag badly on a steep hill. I would use an equation to increase your output, based on your error like Propoutput=(sp-vss)*gain. Ideally you add a time component in this like, add error to errorvar 10 times, then add error to errorvar , multiply the errorvar by .9. then output = Propoutput +errorvar. on every loop it adds the current error and divides to give you the average of 10. This will give you a weighted increasing output as the error increases over time and decrease as the average error decreases.
So the equation is output = Propoutput*gain + average error*gain