Introduction
The following document will provide information to develop your own VACI (Vehicle Accessory Computer Interface). What I have done is rather simple, however it took about 3 weeks of planning, programming and debugging. The wow factor on this interface is awesome; everyone that has seen/used the system wants one in their car. The system can also be modified to automate accessories.
Specifications
- 2X RGB PWM Channels
- 8X General Purpose Outputs
- Support for 5 Channel AV switch. (7 Momentary Outputs)
- RS232 Communication
Requirements
- Software
- Proton PicBasic Compiler
- Serial Bootloader (Preferable)
- VB.NET
- HyperTerminal
- Hardware
- PC
- Pic16F877A
- PicAllw Programmer (only needed if you do not have a Serial Bootloader on your PIC)
- Olimex PIC-P40 Development board (20Mhz)
- 3X ULN2803 Darlington arrays
- Some 1,10,100uF capacitors to reduce noise.
- RS232 Cable
- Tools
Schematic
If you have an Olimex PIC-40 Development board, you shouldn't need a schematic. If you don't, then you only need to build a stable 5V regulator to power the circuit and a RS232 logic level converter so that you can connect the serial of your computer to the serial of the microcontroller. The other important thing is to have all your outputs connected to a ULN2803 Darlington Array IC and remember that the maximum load per output should not exceed 60mA. The ULN2803 is a sinking driver, so you are able to drive your 12V relays from 12VDC. I have also uploaded the musltisim and ultiboard schematic and PCB design files, use them at your own risk. I have not tested the ultiboard design and will not accept any responsibility for it.
My Prototype Hardware


Using the Code
How the RGB software PWM Works

In a nutshell, the RGB software PWM works through an infinite loop with a counter which resets after 255 (one Byte). Each color byte is then compared to the clock, if it is less than the clock, the output is set high. If the color byte is greater than the clock byte, the output is set to low. The color bytes are then changed through a serial data received interrupt.
I then placed the AV switch functions and General Purpose outputs, where the counter resets after reaching 255.
Proton PicBasic Code
Device = 16F877A Declare Bootloader = On Config HS_OSC Xtal = 20 Optimiser_Level = 1 Dead_Code_Remove = On
Hserial_Baud = 9600 Hserial_RCSTA = %10010000 Hserial_Clear = On
All_Digital true
Dim D_Index As Byte Dim D_Byte[12] As Byte Symbol RCIF = PIR1.5
Dim AVclk As Byte
Dim AVMode As Byte
Dim GPO As Byte
Dim clk As Byte
Dim ERGB[3] As Byte Dim EBSO[3] As Byte Dim IRGB[3] As Byte Dim IBSO[3] As Byte
DelayMS 500 INTCON = %11000000 On_Hardware_Interrupt GoTo SerialIn PIE1.5 = 1 D_Index = 0 clk = 0 AVclk = 0 AVMode = 0 GPO = 0 TRISB = %11000000 TRISA = %11000000 TRISE = %11111110 TRISD = %00000000
PORTA = 0
PORTB = 0
PORTD = 0
Clear D_Byte Clear ERGB
Clear EBSO
Clear IRGB
Clear IBSO
Loop:
If clk < ERGB[0] Then
Low PORTB.0
Else
High PORTB.0
EndIf
If clk < ERGB[1] Then
Low PORTB.1
Else
High PORTB.1
EndIf
If clk < ERGB[2] Then
Low PORTB.2
Else
High PORTB.2
EndIf
If clk < IRGB[0] Then
Low PORTB.3
Else
High PORTB.3
EndIf
If clk < IRGB[1] Then
Low PORTB.4
Else
High PORTB.4
EndIf
If clk < IRGB[2] Then
Low PORTB.5
Else
High PORTB.5
EndIf
Inc clk If clk >= 255 Then clk = 0 Inc AVclk If AVMode > 0 Then Select Case AVMode
Case <=32
PORTA = AVMode
Case 64
High PORTE.0
End Select
AVMode = 0
AVclk = 0 EndIf
PORTD = GPO
If AVclk >= 32 Then PORTA = 0 PORTE = 0
AVclk = 0 EndIf
EndIf
GoTo Loop
SerialIn: Int_Sub_Start
INT#STARTH:
Context Save
RECHECK:
If D_Index > 10 Then GoSub clr_Buff
HRSIn D_Byte[D_Index]
HRSOut D_Byte[D_Index]
Inc D_Index
Select D_Byte[0]
Case "A"
If D_Index > 1 Then
AVMode = D_Byte[1]
GoSub clr_Buff
EndIf
Case "G"
If D_Index > 1 Then
GPO = D_Byte[1]
GoSub clr_Buff
EndIf
Case "E"
If D_Index > 6 Then
ERGB[0] = D_Byte[1]
ERGB[1] = D_Byte[2]
ERGB[2] = D_Byte[3]
EBSO[0] = D_Byte[4]
EBSO[1] = D_Byte[5]
EBSO[2] = D_Byte[6]
GoSub clr_Buff
EndIf
Case "I"
If D_Index > 6 Then
IRGB[0] = D_Byte[1]
IRGB[1] = D_Byte[2]
IRGB[2] = D_Byte[3]
IBSO[0] = D_Byte[4]
IBSO[1] = D_Byte[5]
IBSO[2] = D_Byte[6]
GoSub clr_Buff
EndIf
Case Else
GoSub clr_Buff
End Select
If RCIF = 1 Then RECHECK GoTo INT_RETURN clr_Buff:
D_Index = 0 Clear D_Byte Return
INT#ENDH:
INT_RETURN:
Context Restore
Int_Sub_End
Computer Interface Module
The Interface module then takes care of all the communication between the user interface and the microcontroller. Simply insert the following module in your VB.NET project.
VB.NET Module
Imports System
Imports System.IO.Ports Imports System.Drawing
Module VACI
WithEvents COMS As New SerialPort("Com1", 9600, Parity.None, 8, StopBits.One)
Sub Open()
If COMS.IsOpen = False Then
COMS.Encoding = System.Text.Encoding.Default
COMS.Open() End If
End Sub
Sub Close()
If COMS.IsOpen = True Then
COMS.Close()
End If
End Sub
Private Sub COMS_DataReceived(ByVal sender As Object, _
ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
Handles COMS.DataReceived
Dim MyStr As String
MyStr = COMS.ReadExisting
End Sub
Class GlowFx
Shared WriteOnly Property Exterior() As Color
Set(ByVal iColor As Color)
If COMS.IsOpen = True Then
COMS.Write("E" & Chr(iColor.R) & Chr(iColor.G) _
& Chr(iColor.B) & "000" & Chr(234))
End If
End Set
End Property
Shared WriteOnly Property Interior() As Color
Set(ByVal iColor As Color)
If COMS.IsOpen = True Then
COMS.Write("I" & Chr(iColor.R) & Chr(iColor.G) _
& Chr(iColor.B) & "000" & Chr(234))
End If
End Set
End Property
End Class
Class GeneralIO
Private Shared Switch(0 To 7) As Boolean
Shared WriteOnly Property G0() As Boolean
Set(ByVal G0 As Boolean)
Switch(0) = G0
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G1() As Boolean
Set(ByVal G1 As Boolean)
Switch(1) = G1
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G2() As Boolean
Set(ByVal G2 As Boolean)
Switch(2) = G2
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G3() As Boolean
Set(ByVal G3 As Boolean)
Switch(3) = G3
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G4() As Boolean
Set(ByVal G4 As Boolean)
Switch(4) = G4
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G5() As Boolean
Set(ByVal G5 As Boolean)
Switch(5) = G5
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G6() As Boolean
Set(ByVal G6 As Boolean)
Switch(6) = G6
SetGeneralIO()
End Set
End Property
Shared WriteOnly Property G7() As Boolean
Set(ByVal G7 As Boolean)
Switch(7) = G7
SetGeneralIO()
End Set
End Property
Private Shared Sub SetGeneralIO()
Dim GByte As Byte = 0
GByte = IIf(Switch(0), 1, 0) + IIf(Switch(1), 2, 0) + _
IIf(Switch(2), 4, 0) + IIf(Switch(3), 8, 0) + _
IIf(Switch(4), 16, 0) + IIf(Switch(5), 32, 0) + _
IIf(Switch(6), 64, 0) + IIf(Switch(7), 128, 0)
If COMS.IsOpen = True Then
COMS.Write("G" & Chr(GByte) & Chr(234))
End If
End Sub
End Class
Class AVSwitch
Shared Sub TogglePWR()
If COMS.IsOpen = True Then
COMS.Write("A" & Chr(64) & Chr(234))
End If
End Sub
Shared Sub ToggleAV()
If COMS.IsOpen = True Then
COMS.Write("A" & Chr(32) & Chr(234))
End If
End Sub
Shared Sub SetAVChan(ByVal AVChan As Byte)
If COMS.IsOpen = True Then
Select Case AVChan
Case 1
COMS.Write("A" & Chr(1) & Chr(234))
Case 2
COMS.Write("A" & Chr(2) & Chr(234))
Case 3
COMS.Write("A" & Chr(4) & Chr(234))
Case 4
COMS.Write("A" & Chr(8) & Chr(234))
Case 5
COMS.Write("A" & Chr(16) & Chr(234))
End Select
End If
End Sub
End Class
End Module
To initialize the communication, simply place the following code in your project, only remember to close the serial communication on exit. If you don't disable the serial communication when you exit the application, there will be a problem initializing serial port if the application is reopened.
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
VACI.Open()
End Sub
Private Sub Form1_FormClosing(ByVal sender As Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) _
Handles Me.FormClosing
VACI.Close()
End Sub
To toggle the power of the AV switch, use the following code:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
VACI.AVSwitch.TogglePWR()End Sub
To set the AV switch channel, use the following code:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
VACI.AVSwitch.SetAVChan(1)
End Sub
The following code is used to set the General Purpose outputs G0 through G7, TRUE is for on and FALSE for off.
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
VACI.GeneralIO.G3 = True
End Sub
The following code is used to set the RGB Glow Channel 1 as color:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
VACI.GlowFx.Exterior = Color.GreenYellow
End Sub
The following code is used to set the RGB Glow Channel 2 as color:
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
VACI.GlowFx.Interior = Color.OrangeRed
End Sub
Test.
Points of Interest
I have been programming Pic16Fs for over 7 years, and I got bored of manually flipping switches on my jeep. So I decided to combine my experience of programming and hardware to develop this application to ease my accessory usage in my jeep.
My aim was to integrate all my accessories into a central system, I was given a Lilliput PC745 as a gift and decided to use that. In my solution, I have used the PC745’s serial port to communicate with the PIC 16F877A.
The most complex problem that had to be solved is the 6 PWM (Pulse Width Modulation) channels that were needed to run the two RGB LED channels. The 16F877A only has two, so I opted for a type of software PWM. This meant that the first thing I had to get working was the software PWM along with the serial communication.
This took several attempts with different resolutions, refresh rates and so on. I experimented with different clock frequencies. Finally, it came to an 8bit resolution, with an unknown refresh rate. The best solution with the least amount of flicker came to be by doing an infinite loop with no delays and turning each of the pins on/off for the desired time.
The next task was to fit the General Purpose output pins in this infinite loop without delay, or else the RGB glow would flicker. The best way to do that is simply assign an entire port at the same time through one byte. So the PC would have to generate a character for the desired pins to be switched on individually. Each bit in the byte is a pin on the microcontroller which makes life a bit easier.
I then had an off-the-shelf AV switch to select my AV input channels; it switches by momentary push button. So I combined PortE.0 with PortA, as PortA only has 6 pins and I needed 7. The tricky part was to create a momentary switch without delaying PWM channels. So to do this, I stuck my code in at the end of the For Loop that refreshes the RGB channels. So that it switches high at the end of the update, then waits for 32 RGB refresh cycles before clearing the port. This can be modified very easily to be used as General Purpose Outputs.
History
- First working version - 22/09/2010
My love for imagining, designing and creating just about anything REAL or VIRTUAL has lead me on this path. Whether it be 1’s and 0’s, soldering an IC to a PCB or getting under the car just to get my hands dirty, I like to do it DIY style.
Life is a journey, and it’s all about learning and creating…
Some Achievements in my life:
• Founder of the Doha College Robotics Club
• President and founder of the CNA-Q Robotics Club
• Developed Fly-By-Wire Over Ethernet (FBWOE) for boats.
• Telecommunication Engineering Technology Diploma
• X10 Home Automation system as part of a Green Home project
• Developed a Vehicle Accessory Computer Interface (VACI)
• Autonomos Turtle Monitoring Robot
• Virtual Autonomous Remote Presence (VARP)