PDA

View Full Version : Single PIC to Blink 5 LEDs Independently?



RossWaddell
- 26th October 2012, 22:05
I'm in the final stages of a project I've been working off-and-on for the past 8 months. The final stage before submitting my board is to see if I can reduce the number of components by combining functionality into 1 chip.

Currently, I have 5 12F629's which each blink 1 LEDs with separate on/off timings, with some randomization in the on/off cycles. I use PWM to fade in/out the LEDs as they must simulate old incandescent Christmas tree lights. Can I do this all with 1 pic (say, 16F1825)? I've been looking at Darrel's SPWM_INT - Multiple Software PWM (http://darreltaylor.com/DT_INTS-14/SPWM.html) but not sure if I can code in the needed PAUSEs while the LEDs are fully on or off (to get the right blinky look).

Am I on the right path here, or does anyone have some suggestions for me? Going from 5 chips to 1 would be a big win for me.

Demon
- 27th October 2012, 00:33
I went on Microchip and looked for a PIC with at least 5 timers: PIC18F24J11.

http://www.microchip.com/ParamChartSearch/chart.aspx?branchID=1004&mid=10&lang=en&pageId=74

(That page might load properly, or not.)

I'd try to use that along with DT-INTs; best way to blink LEDs.

Robert

EDIT:

http://darreltaylor.com/DT_INTS-18/home.html


TMR0_INT -- TMR0 Overflow
TMR1_INT -- TMR1 Overflow
TMR2_INT -- TMR2 to PR2 Match
TMR3_INT -- TMR3 Overflow
TMR4_INT -- TMR4 Overflow



EDIT SOME MORE:

There are 16F models if that makes things easier for you: PIC16F1782

http://www.microchip.com/ParamChartSearch/chart.aspx?branchID=1002&mid=10&lang=en&pageId=74#

Use the Show/Hide Column feature to speed browsing.

RossWaddell
- 27th October 2012, 01:16
Thanks Robert. 16Fs do make things easier for me. Looks like the 16F1782 has (4) 8bit timers and (1) 16bit timer so I'll pick up some of those.

I'm wondering, though, if I could use a 12F683 (2 x 8-bit, 1 x 16-bit) and the unused ports of a 16F1825 (I have RC4 and RA3-5 ports available) which has 4 x 8-bit and 1 x 16-bit timers. Some of the TMRs on the 16F1825 are already used, I'm sure, since I'm using CCP1 & CCP2.

How can I tell which TMRs are being used? Here's my code:



'************************************************* ***************
'* Name : Nacelle_Motors_16F1825_32Mhz_Int_SN754410.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2012 *
'* : All Rights Reserved *
'* Date : 10/20/2012 *
'* Version : 2.0 *
'* Notes : Motor control for TOS E engines using SN754110 *
'* : motor driver, plus steady-on amber lights *
'************************************************* ***************

' ************************************************** *************
' TODOs
' ************************************************** *************
' 1. Need to calibrate motor RPMs so they spin at the same rate
' -> Separate "MotorRPM" EEPROM variables?
' 2. Add power isolation as blinking running lights seem to increase electrical noise

DEFINE OSC 32 ' Set oscillator 32 Mhz

' ************************************************** *************
' Device Fuses
' ************************************************** *************
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_ON & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Initialization
' ************************************************** *************

OSCCON = %11110000 ' 32 MHz internal osc (8 Mhz with 4x SPLLEN enabled)

pause 100 ' As advised by Darrel Taylor for EEPROM issue

DEFINE CCP2_REG PORTC ' Only need to define CCP2 pin as there are 2
DEFINE CCP2_BIT 3 ' possible choices: RA5 or RC3

PORTA = 0 ' Set initial value of PORTA to 0
PORTC = 0 ' Set initial value of PORTC to 0
;ADCON0 = 7
;ADCON1 = 7
ANSELA.0 = 0 ' Digital only on rotary encoder B pin
ANSELA.1 = 0 ' Digital only on rotary encoder A pin
ANSELA.2 = 0 ' Digital only on rotary encoder's button
ANSELC.0 = 0 ' Digital only on Stbd motor direction signal (Motor 1)
ANSELC.1 = 0 ' Digital only on Port motor direction signal (Motor 2)
ANSELC.2 = 0 ' Digital only on LED_0 pin (steady-on amber lights)
ANSELC.3 = 0 ' Digital only on CCP2 pin (Port engine)
ANSELC.5 = 0 ' Digital only on CCP1 pin (Stbd engine)

TRISA = %00000111 ' Make PORTA pins 0-2 input for rotary encoder
TRISC = %00000000 ' Make all PORTC pins output

' The INTEDG bit of the OPTION_REG register determines on which edge the
' interrupt will occur. When the INTEDG bit is set, the rising edge will
' cause the interrupt. When the INTEDG bit is clear, the falling edge will
' cause the interrupt.
OPTION_REG.6 = 1 ' 1=Rising edge (default) or button "PRESS";
' 0=Falling edge or button "RELEASE"

Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortA.1;
' Rot Enc pin B connected to PortA.0
Old_RPM VAR BYTE
I VAR BYTE

LED_0 VAR PORTC.2 ' Alias PORTC.2 as "LED_0"

MOTOR_1_DIR VAR PORTC.0 ' Alias PORTC.0 as "MOTOR_1_DIR"
MOTOR_1_PWM VAR PORTC.5 ' Alias PORTC.5 as "MOTOR_1_PWM"
MOTOR_2_DIR VAR PORTC.1 ' Alias PORTC.1 as "MOTOR_2_DIR"
MOTOR_2_PWM VAR PORTC.3 ' Alias PORTC.3 as "MOTOR_2_PWM"

ButtonPress VAR PORTA.2 ' Alias PORTA.2 as "ButtonPress"
TimeCnt VAR Word

LOW LED_0
LOW MOTOR_1_DIR
LOW MOTOR_1_PWM
LOW MOTOR_2_DIR
LOW MOTOR_2_PWM

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' RA0 -> B pin of rotary encoder
' RA1 -> A pin of rotary encoder
' RA2 -> Button switch pin of rotary encoder
' RC0 -> Stbd motor direction signal (motor 1)
' RC1 -> Port motor direction signal (motor 2)
' RC2 -> Steady-on LED
' RC3/CCP2 -> Port motor PWM output (motor 2)
' RC5/CCP1 -> Stbd motor PWM output (motor 1)

' ************************************************** *************
' Includes
' ************************************************** *************

INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp3)
INCLUDE "EE_Vars.PBP" ' copy file to PBP main folder (i.e. c:\pbp3)

' ************************************************** *************
' EEPROM Variables
' ************************************************** *************

MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 150
PortEngDir VAR BYTE : @ EE_var _PortEngDir, BYTE, 0

' ************************************************** *************
' ASM Interrupt Definitions
' ************************************************** *************

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler IOC_INT, _RotEncAdjust, PBP, yes
; INT_Handler INT_INT, _RotEncBtnPress, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

' ************************************************** *************
' Set default values
' ************************************************** *************

Old_RPM = MotorRPM
Old_Bits = PORTA & (%00000011)

HIGH LED_0 ' Turn on steady-on LEDs

LOW MOTOR_1_DIR ' Set stbd motor (motor 1) to fwd (CW)
' (always spins in the same direction)
IF PortEngDir = 0 THEN
LOW MOTOR_2_DIR ' Set port motor (motor 2) to fwd (CCW)
ELSE
HIGH MOTOR_2_DIR ' Set port motor (motor 2) to rev (CW)
ENDIF

' Spin up motors to saved value of _MotorRPM
' (Below a cycle of 66, the motors don't move at all)
IF MotorRPM > 66 THEN
FOR I = 65 to MotorRPM
pause 30
HPWM 1, I, 20000 ' Stbd engine (CCP1)
IF PortEngDir = 0 THEN
HPWM 2, I, 20000 ' Port engine (CCP2), fwd (CCW)
ELSE
HPWM 2, (255-I), 20000 ' Port engine (CCP2), rev (CW)
ENDIF
NEXT I
EndIf
HPWM 1, MotorRPM, 20000 ' Stbd engine (CCP1)
IF PortEngDir = 0 THEN
HPWM 2, MotorRPM, 20000 ' Port engine (CCP2)
ELSE
HPWM 2, (255-MotorRPM), 20000 ' Port engine (CCP2)
ENDIF

' Enable interrupts on RPM control now that motors are fully spun up
;INTCON = %10011000 ' Global int enabled, INTE enabled, IOCI enabled,
' INTF flag bit 0 clr, IOCI flag bit 0 clr
INTCON = %10001000 ' Global int enabled, IOCI enabled,
' INTF flag bit 0 clr, IOCI flag bit 0 clr
IOCAP.0 = 1 ' Enable positive (rising edge) change
IOCAP.1 = 1 ' Enable positive (rising edge) change
IOCAN.0 = 1 ' Enable negative (falling edge) change
IOCAN.1 = 1 ' Enable negative (falling edge) change
IOCAF.0 = 0 ' Clear interupt-on-change flag
IOCAF.1 = 0 ' Clear interupt-on-change flag
;INTCON.1 = 0 ' Clear RA2/INT External Interrupt Flag

@ INT_ENABLE IOC_INT ; Interrupt-on-Change interrupt
;@ INT_ENABLE INT_INT ; INT/Ext Interrupt

Main:
' Check if motor RPM has changed
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB ChngMotorHPWM
EndIF

TimeCnt = 0
While ButtonPress = 0
TimeCnt = TimeCnt + 1
Pause 10
If TimeCnt > 500 Then BtnAction
Wend

BtnAction:
If TimeCnt > 0 and TimeCnt < 200 Then
PortEngDir = PortEngDir + 1
If PortEngDir > 1 Then PortEngDir = 0
@ EE_write_var _PortEngDir ; save the new number to EEPROM

GOSUB ReversePortEng
ElseIf TimeCnt >= 200 Then
@ EE_write_default _MotorRPM
EndIf

GOTO Main

ReversePortEng:
LOW MOTOR_2_DIR ' Electrically stop motor 2
LOW MOTOR_2_PWM

pause 100

IF PortEngDir = 0 THEN
LOW MOTOR_2_DIR ' Set port motor (motor 2) to fwd (CCW)
ELSE
HIGH MOTOR_2_DIR ' Set port motor (motor 2) to rev (CW)
ENDIF

IF PortEngDir = 0 THEN
HPWM 2, MotorRPM, 20000 ' Port engine (motor 2), fwd (CCW)
ELSE
HPWM 2, (255-MotorRPM), 20000 ' Port engine (motor 2), rev (CW)
ENDIF

RETURN


ChngMotorHPWM:
HPWM 1, MotorRPM, 20000 ' Stbd engine (motor 1)
; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing

IF PortEngDir = 0 THEN
HPWM 2, MotorRPM, 20000 ' Port engine (motor 2), fwd (CCW)
ELSE
HPWM 2, (255-MotorRPM), 20000 ' Port engine (motor 2), rev (CW)
ENDIF

RETURN
end

' ************************************************** *************
' [IOC - interrupt handler]
' ************************************************** *************
RotEncAdjust:
New_Bits = PORTA & (%00000011)
IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc

RotEncDir = New_Bits.1 ^ Old_Bits.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF

@ EE_write_var _MotorRPM ; save the new number to EEPROM

DoneRotEnc:
Old_Bits = New_Bits

IOCAF.0 = 0 ' Clear interrupt flags
IOCAF.1 = 0

@ INT_RETURN

' ************************************************** *************
' [INT - interrupt handler]
' ************************************************** *************
;RotEncBtnPress:
' Restore MotorRPM to default value
;@ EE_write_default _MotorRPM

; INTCON.1 = 0 ' Clear RA2/INT External Interrupt Flag

;@ INT_RETURN



6725

Also, would adding the blinkies to this project (which already uses Darrel's DT_INST-14.bas to handle a quadrature rotary encoder inputs) affect the existing functionality?

RossWaddell
- 27th October 2012, 01:26
Do you see any (major) difference between the 16G1782 and the 16F1825? The latter has only 14 pins (compared to the former's 28) and that would help a lot; I only need 5 outputs. Plus, I have a bunch of those around.

Ok, so let's say I use 1 dedicated 16F1825 chip to do the 5 independent blinks - should I use the SPWM include from Darrel? How do I configure the 5 timers with different on/off frequencies?

rmteo
- 27th October 2012, 06:58
How to blink 8 LEDs at different rates- concurrently? (http://www.picbasic.co.uk/forum/showthread.php?t=13167)

grahamg
- 27th October 2012, 07:36
You do not need a seperate hardware timer for each led. A single timer would do the job.:smile: Adapt the following code to suit your needs.


#config
__CONFIG _CONFIG1, _LVP_OFF & _FCMEN_ON & _IESO_ON & _BOR_ON & _CPD_OFF & _CP_OFF & _MCLRE_ON & _PWRTE_ON & _WDT_ON & _XT_OSC
__CONFIG _CONFIG2, _WRT_OFF & _BOR40V
#endconfig

TIMER0 VAR BYTE
TIMER1 VAR BYTE
TIMER2 VAR BYTE
TIMER3 VAR BYTE
TIMER4 VAR BYTE
TIMER5 VAR BYTE
TIMER6 VAR BYTE
TIMER7 VAR BYTE

LED0 VAR PORTB.0
LED1 VAR PORTB.1
LED2 VAR PORTB.2
LED3 VAR PORTB.3
LED4 VAR PORTB.4
LED5 VAR PORTB.5
LED6 VAR PORTB.6
LED7 VAR PORTB.7

wsave VAR BYTE $70 SYSTEM
ssave VAR BYTE $71 SYSTEM
psave VAR BYTE $72 SYSTEM


GOTO START:
DEFINE INTHAND MYINT
ASM
MYINT
movwf wsave ; <=2k codespace
swapf STATUS,W ; <=2k codespace
clrf STATUS ; <=2k codespace
movwf ssave ; <=2k codespace
movf PCLATH,W ; <=2k codespace
movwf psave ; <=2k codespace
TSTF _TIMER0 ;test timer0
BTFSS STATUS,Z ;skip if zero
DECF _TIMER0,F ;decrement timer 0
TSTF _TIMER1
BTFSC STATUS,Z
DECF _TIMER1,F
TSTF _TIMER2
BTFSC STATUS,Z
DECF _TIMER2,F
TSTF _TIMER3
BTFSC STATUS,Z
DECF _TIMER3,F
TSTF _TIMER4
BTFSC STATUS,Z
DECF _TIMER4,F
TSTF _TIMER5
BTFSC STATUS,Z
DECF _TIMER5,F
TSTF _TIMER6
BTFSC STATUS,Z
DECF _TIMER6,F
TSTF _TIMER7
BTFSC STATUS,Z
DECF _TIMER7,F
BCF PIR1,0
MOVF psave,W
MOVWF PCLATH
SWAPF ssave,W
MOVWF STATUS
SWAPF wsave,F
SWAPF wsave,W
RETFIE
ENDASM

START:
ANSEL=0 'all pins are digital
ANSELH=0
TRISB=$00 'all outputs
PORTB=0
OPTION_REG=$80
T1CON=$01 'interrupt every 65.5 ms
PIE1=$01 'enable timer 1 interrupts
INTCON=$C0 'enable interrupts
AGAIN:
IF TIMER0=0 THEN
TIMER0=10
TOGGLE LED0
ENDIF
IF TIMER1=0 THEN
TIMER1=9
TOGGLE LED1
ENDIF
IF TIMER2=0 THEN
TIMER2=5
TOGGLE LED2
ENDIF
IF TIMER3=0 THEN
TIMER3=7
TOGGLE LED3
ENDIF
IF TIMER4=0 THEN
TIMER4=12
TOGGLE LED4
ENDIF
IF TIMER5=0 THEN
TIMER5=6
TOGGLE LED5
ENDIF
IF TIMER6=0 THEN
TIMER6=8
TOGGLE LED6
ENDIF
IF TIMER7=0 THEN
TIMER7=11
TOGGLE LED7
ENDIF
GOTO AGAIN

pic used is a 16F882.

RossWaddell
- 27th October 2012, 17:13
I will indeed try that out with a 1fF1825. Could you please clarify for me where I set the individual on/off values for each of the 8 LEDs? In my existing implementation (5 separate 12F683's) I have code like this:



' Changing the value for "Cycle" will change the time it takes
' to fade from 100% to 0% (or 0% to 100% if duty is increasing)
' Lower number = faster fade in/out
Cycle = 1

' Higher step value produces faster fade, but too high a value
' will introduce flicker
STEP_CNTR = 2

Main:
' Fade in
For Duty = 0 TO 255 step STEP_CNTR
PWM LED_0, Duty, Cycle
Next

' Stay on LGHTS_ON_MS
HIGH LED_0
Pause LGHTS_ON_MS

' Fade out
For Duty = 255 TO 0 STEP -STEP_CNTR
PWM LED_0, Duty, Cycle
Next

' Stay off for LGHTS_OFF_MS
Pause LGHTS_OFF_MS

' Fade in
For Duty = 0 TO 255 step STEP_CNTR
PWM LED_0, Duty, Cycle
Next

' Stay on LGHTS_ON_MS
HIGH LED_0
Pause (LGHTS_ON_MS - 200)

' Fade out
For Duty = 255 TO 0 STEP -STEP_CNTR
PWM LED_0, Duty, Cycle
Next

' Stay off for LGHTS_OFF_MS
Pause (LGHTS_OFF_MS + 100)

GOTO Main


(In actuality, I extend the fade in/stay on/fade out/stay off with various timings to randomize the effect. Old 1960's Christmas tree lights did not have constant blink rates).

RossWaddell
- 28th October 2012, 18:31
I've tried adapting Darrel's example from this (http://www.picbasic.co.uk/forum/showthread.php?t=13167&p=88980#post88980) thread, but no luck with a PIC16F1825 - PORTC.0-3 do not blink (PORTC.0-2 are off; PORTC.3 is on) but PORTC.4 blinks properly. What am I doing wrong here? I've had to adapt Darrel's code for the PIC16F1825 and reduced the number of blinkies to 5 from 8.



'************************************************* ***************
'* Name : UNTITLED.BAS *
'* Author : Ross Waddell *
'* Notice : Copyright (c) 2012 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 10/27/2012 *
'* Version : 1.0 *
'* Notes : PIC16F1825 *
'* : *
'************************************************* ***************

DEFINE OSC 4
DEFINE BLINKYFREQ 100 ; 10mS periods

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

OSCCON = %01101000 ' 4MHz internal osc


DATA 50,22,38,75,17;,40,62,13 ; default periods for each Output
;----------------------------------------------------------------
CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[5]
LoopCON VAR BYTE[5]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to 4 ; load the periods from EEPROM
READ x, LoopCON(x)
NEXT X
;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

ANSELC = 0
TRISC = 0 ; PORTC all OUTPUT

;----[Main Program Loop]----------------------------------------
Main:
;x = (x + 1) // 8
x = (x+1)&4
PORTC.0(x) = !(LoopLED(x) < LoopCON(x))
LoopLED(x) = (LoopLED(x) + 1) // (LoopCON(x) << 1)
IF x != 4 THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

RossWaddell
- 28th October 2012, 18:39
Ok, so I answered my own question.

This works:


x = (x + 1) // 5


This doesn't:


x = (x+1)&4


The question now becomes: is there a way to set the on/off periods individually for each of the 5 LEDs? The code above has the LEDs blink with the same on time as off; I need to be able to make some of the 'off' times longer than the 'on' and vice versa.

Darrel Taylor
- 28th October 2012, 22:09
The question now becomes: is there a way to set the on/off periods individually for each of the 5 LEDs?
Yes, but you have to create the individual ON/OFF periods.
That code only has 1 period, for a 50% dutycycle.

So many things you can do.
So little time to explain.
Twinkle Twinkle little tree ...

DEFINE OSC 4
DEFINE BLINKYFREQ 100 ; 10mS periods

' ************************************************** *************
' Device Fuses
' ************************************************** *************
#CONFIG
__config _CONFIG1, _FOSC_INTOSC & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

OSCCON = %01101000 ' 4MHz internal osc

;----------------------------------------------------------------
LEDcount CON 5 ; Number of LEDs on the PORT
OnTimes DATA 50,22,38,75, 5 ; default periods for each Output
OffTimes DATA 150,45,38,95,34

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 10 ; Minimum random ON time
MAX_ON CON 100 ; Maximum random ON time
MIN_OFF CON 10 ; Minimum random OFF time
MAX_OFF CON 100 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF

;----------------------------------------------------------------
CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to LEDcount - 1 ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

ANSELC = 0
TRISC = 0 ; PORTC all OUTPUT

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
PORTC.0(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

RossWaddell
- 29th October 2012, 00:00
My god ... it's ... it's beautiful :) Just like a 1960's string of Christmas tree lights, the exact effect I'm after. I can't thank you enough, Darrel!

One last query (I hope) - since I only need 5 output pins I would like to convert this to a 12F683. When I do, I get the 'ol "Redefinition of VAR" error pointing to this line:



CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L


The whole code is here:



'************************************************* ***************
'* Name : UNTITLED.BAS *
'* Author : Ross Waddell *
'* Notice : Copyright (c) 2012 [select VIEW...EDITOR OPTIONS] *
'* : All Rights Reserved *
'* Date : 10/27/2012 *
'* Version : 1.0 *
'* Notes : PIC12F683 *
'* : *
'************************************************* ***************

DEFINE OSC 4
DEFINE BLINKYFREQ 100 ; 10mS periods

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite
#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOD_ON & _CP_OFF & _CPD_OFF
#ENDCONFIG

OSCCON = %01100000 ' 4MHz internal osc

;----------------------------------------------------------------
LEDcount CON 5 ; Number of LEDs on the PORT
OnTimes DATA 50,22,38,75,17 ; default periods for each Output
OffTimes DATA 150,45,38,95,117

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 15 ; Minimum random ON time
MAX_ON CON 115 ; Maximum random ON time
MIN_OFF CON 15 ; Minimum random OFF time
MAX_OFF CON 200 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF

;----------------------------------------------------------------
CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.5

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to (LEDcount - 1) ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

ANSEL = 0
TRISIO = 0 ; PORTC all OUTPUT

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
GPIO.0(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main


Since there are no include files, my Google search didn't point to anything other than that. Is this too much to expect from a 12F?

grahamg
- 29th October 2012, 09:35
Looking at the compiler include file for a 12f683 the CCPR1 VAR WORD EXT has already been included. I think you should be able to comment this line out.
Can you please tell me what the following line of code does:
GPIO.0(x) = !!(LoopLED(x) < OnTime(x))
Cannot find any reference to !!

Darrel Taylor
- 29th October 2012, 15:39
Can you please tell me what the following line of code does:
GPIO.0(x) = !!(LoopLED(x) < OnTime(x))
It is equivelent to ...

IF LoopLED(x) < OnTime(x) THEN
GPIO.0(x) = 1
ELSE
GPIO.0(x) = 0
ENDIF


What you would like to do is a direct assignment of a True/False comparison to a variable.
GPIO.0(x) = LoopLED(x) < OnTime(x)
But LoopLED(x) < OnTime(x) is a "Logical" expression that can't be assigned to a BIT variable.

The bitwise NOT operator (!) can convert the logical expression to a bitwise expression that can be assigned to a BIT variable.

A single ! will invert the result, so a second ! is used to invert it back.
Somtimes you want the result inverted, and you can use a single !.
Other times you might invert the logic of the comparison, use a single !, which gives you a non-inverted result.

RossWaddell
- 29th October 2012, 15:54
Darrel - do you also think I can simply comment out this line to compile for a 12F629?

CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L

Acetronics2
- 29th October 2012, 17:48
Hi,

I think you also have to add some lines very specific for the used chip ...

especially to deal with GPIO.3 ... which is input only pin ... ;)

Alain

RossWaddell
- 29th October 2012, 18:08
I overlooked that GP3 is input only, Alain. So the 12F629 has only 5 output pins. That would still work for the 5 blinky LEDs but I've been expanding Darrel's example to include 2 other non-random blinky LEDs in order to further reduce the # of components, so if I end up with 6-7 different blink rates I might as well stay with the 16F1825 (since there doesn't appear to be any PICs with 10-12 pins).

SteveB
- 30th October 2012, 04:51
My god ... it's ... it's beautiful :)

Yes, the code is beautiful (can speak for the lights, but I'm sure their nice too).

Have you seen one of Darrel's breadbord layouts? Those are equally as beautiful.

Jerson
- 30th October 2012, 12:12
It is equivelent to ...

IF LoopLED(x) < OnTime(x) THEN
GPIO.0(x) = 1
ELSE
GPIO.0(x) = 0
ENDIF



Like any invention, after the explanation, it seems obvious. However, this is a coding construct I loved learning.

Ioannis
- 30th October 2012, 12:53
Cool! The man is an artist!

Kudos to Darrel.

Ioannis

Mike, K8LH
- 30th October 2012, 15:00
It is equivelent to ...

IF LoopLED(x) < OnTime(x) THEN
GPIO.0(x) = 1
ELSE
GPIO.0(x) = 0
ENDIF


What you would like to do is a direct assignment of a True/False comparison to a variable.
GPIO.0(x) = LoopLED(x) < OnTime(x)
But LoopLED(x) < OnTime(x) is a "Logical" expression that can't be assigned to a BIT variable.

The bitwise NOT operator (!) can convert the logical expression to a bitwise expression that can be assigned to a BIT variable.

A single ! will invert the result, so a second ! is used to invert it back.
Somtimes you want the result inverted, and you can use a single !.
Other times you might invert the logic of the comparison, use a single !, which gives you a non-inverted result.

Could the comparison have been reversed, like this?



GPIO.0(x) = !(LoopLED(x) >= OnTime(x))


Also, is the GPIO.0(x) a valid PBP construct? It's not very intuitive (to me). I would think that GPIO.0 is the GP0 pin and then the (x) looks really alien...

Darrel Taylor
- 30th October 2012, 16:17
Thanks guys, but I learned the !! from Charles Leo (my boss).
It's great to be in the offices of melabs. :)

Mike,

Yup, that's the correct way to "invert the logic of the comparison, use a single !, which gives you a non-inverted result.".

And while it does look strange, the (x) in GPIO.0(x) is the offset from GPIO.0.
If x = 0 then it uses GPIO.0
If x = 2, it uses GPIO.2

You can start from any bit.
With GPIO.2(x)

If x = 0 then it uses GPIO.2
If x = 2, it uses GPIO.4

This notation can only be used in direct assignments (using = sign).
You cannot use it as PIN designators in PBP commands like SERIN/OUT, HIGH/LOW etc.

RossWaddell
- 30th October 2012, 18:29
No argument from me, SteveB - the elegance of the solution is indeed beautiful!

Would I gain any better resolution/exactitude of timings by increasing the oscillator? The 16F1825 can go up to 32Mhz internal with the pullup enabled. I've added to Darrel's solution to include another blinky without randomization of the periods, so it needs to be on for 20ms and off for 480ms (i.e. it blinks twice a second).

Darrel Taylor
- 30th October 2012, 21:06
Would I gain any better resolution/exactitude of timings by increasing the oscillator?

The timing is maintained by the CCP module and Timer1. Those periods will remain the same reqardless of the CPU's frequency.
Increasing the frequency will only allow more instructions to be executed between periods.

The way I left it, only 4.5 mS of the 10 mS periods are being used to run the 5 led's.
You should be able to add 2 more without any problems.

RossWaddell
- 31st October 2012, 03:41
I switched to a PIC16F88 since I needed a chip which had 8 pins of the same port (i.e. PORTB0-7). I thought I had it working using RB0-6 (7 blinkies) but realized this chip has the external interrupt on RB0, which I will need for the button to toggle 'FlashMode'. So I moved everything over to PORTA and now the LED connected to RA7 kind of flickers and is no way near the 1.5 sec on/0.5 sec off flash rate set in code.



'************************************************* ***************
'* Name : Formation_Engine_Running_Lights_16F88_4Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2012 *
'* : All Rights Reserved *
'* Date : 10/29/2012 *
'* Version : 1.0 *
'* Notes : Strobe lights, nacelle lights (steady-on and *
'* Christmas flashers) and running lights for the *
'* TOS Enterprise *
'* : *
'************************************************* ***************

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' RA0 -> Flashing lights for nacelle engines 1 (x2)
' RA1 -> Flashing lights for nacelle engines 2 (x2)
' RA2 -> Flashing lights for nacelle engines 3 (x2)
' RA3 -> Flashing lights for nacelle engines 4 (x2)
' RA4 -> Flashing lights for nacelle engines 5 (x2)
' RA5 -> Dummy output (MCLR is input only)
' RA6 -> Secondary hull strobe/formation lights (x2)
' RA7 -> Primary hull running lights (x4)
' RB0 -> Button for toggling running light flash mode
' RB1 -> Steady-on lights for nacelle engines (x10)

' Secondary Hull Strobes Lights
' =============================
' The two strobe locastions are:
' - Port/starboard rear secondary hull (just forward of the "1837" marking),
' horizontally inline with the shuttlebay flight deck

' The strobe light flashed at the rate of twice per second,
' or every 12 film frames. Since it's a strobe, it stays on
' for much less time than a running light; approx. .020 sec

' Nacelle engine lights
' =====================
' (a) The colours used were: red, blue, green, amber and pink, all standard colours
' they had for common Christmas lights of the time.
' (b) They were all Christmas lights (C7, too big for a 1/350).
' (c) Amber was steady (5 in a star pattern), the other 5 blinked at various rates

' Running lights
' ==============
' The running light locations on the primary hull are (4):
' - Saucer Port [top] (red) - 1
' - Saucer Starboard [top] (green) - 1
' - Saucer Port/Starboard [bottom (white) - 2

' The TOS E running lights flash at a base timing of 1-1/2 sec (36 frames) on,
' 1/2 sec (12 frames) off (FlashMode = 0)

' Alternate is 1/2 sec on, 1.5 sec off (reverse of above). (FlashMode = 1)

' In "The Corbomite Maneuver", the lights were on for 18 frames and off for 20
' (FlashMode = 2)

' >> These modes are available via a momentary button

DEFINE OSC 4 ; Set oscillator 4Mhz
DEFINE BLINKYFREQ 100 ; 10mS periods

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#CONFIG
__config _CONFIG1, _FOSC_INTOSCIO & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Initialization
' ************************************************** *************

OSCCON = %01100000 ; 4MHz internal osc

ANSEL = 0 ; PIC16F88 only
TRISA = 0 ; PORTA all output
TRISB = %00000001 ; Make PORTB pin 0 input for flash mode button

LEDcount CON 8 ; Number of blinking LEDs on PORTA
' BL1,BL2,BL3,BL4,BL5,MCLR,STB
OnTimes DATA 50 ,22 ,38 ,75 ,17 ,2 ,2 ; default "on" periods for each output
OffTimes DATA 150 ,45 ,38 ,95 ,117,2 ,48 ; default "off" periods for each output

; (Strobe flashes 2 times per second
; or every 500ms; subtract "on" time)

' Running lights will be handled slightly differently

' FlashMode Values
' ================
' 0 = 1-1/2 sec (36 frames) on, 1/2 sec (12 frames) off (Trek Ace, HobbyTalk)
' 1 = 1/2 sec (12 frames) on, 1-1/2 sec (36 frames) off (Master Replica model)
' 2 = 3/4 sec (18 frames) on, 5/6 sec (20 frames) off ("The Corbomite Maneuver")

FlashMode_Default CON 0
EE_FlashMode DATA FlashMode_Default
FlashMode VAR BYTE
READ EE_FlashMode, FlashMode

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE ; randomization used for BL1-5 only
RND VAR WORD : RND = 13864
MIN_ON CON 15 ; Minimum random ON time
MAX_ON CON 115 ; Maximum random ON time
MIN_OFF CON 15 ; Minimum random OFF time
MAX_OFF CON 200 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount-3]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF

CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
;CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

LED_STDY_ON VAR PORTB.1 ; Alias PORTB.1 as "LED_STDY_ON"

;Old_FlashMode VAR BYTE
PRI_HULL_LGHTS_ON_MS VAR BYTE
PRI_HULL_LGHTS_OFF_MS VAR BYTE

;----[Initialize on/off periods & random sequencing (if enabled)]---------------
; (only set up first 7 blinkies; the 8th [running lights] is separate)
FOR x = 0 to (LEDcount - 2) ; Load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
ENDIF
#ENDIF
NEXT X

GOSUB SetFlashRates ; Set up flash rates based on saved "FlashMode" val

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

HIGH LED_STDY_ON ; Turn on steady-on LEDs

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
PORTA.0(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

SetFlashRates:
If FlashMode = 0 Then
PRI_HULL_LGHTS_ON_MS = 150
PRI_HULL_LGHTS_OFF_MS = 50
ElseIf FlashMode = 1 Then
PRI_HULL_LGHTS_ON_MS = 50
PRI_HULL_LGHTS_OFF_MS = 150
Else
PRI_HULL_LGHTS_ON_MS = 75
PRI_HULL_LGHTS_OFF_MS = 83
EndIf

OnTime(8) = PRI_HULL_LGHTS_ON_MS
OffTime(8) = PRI_HULL_LGHTS_OFF_MS

RETURN
END


What am I doing wrong? In case anyone is wondering, I only want to randomize RA0-4 flash rates. Also, RA5 (MCLR) is input only so I had to make that a dummy output pin.

Is there something special about RA7 because of Timer1?

Acetronics2
- 31st October 2012, 09:27
Not really ...



OnTimes DATA 50 ,22 ,38 ,75 ,17 ,2 ,2 ; default "on" periods for each output
OffTimes DATA 150 ,45 ,38 ,95 ,117,2 ,48 ; default "off" periods for each output


nothing missing here ??? :rolleyes:

Alain

RossWaddell
- 31st October 2012, 12:05
Alain - not sure what you're referring to. Although I'm using 8 LEDs the 8th one is set up differently. You can see that farther done in the code with the GOSUB. The second last '2's are for the dummy MCLR pin. Rolls eyes.

Darrel mentioned that the 'PORTB.0(x)' notation represents the offset from RB0, so maybe I'll try reverting to PortB but use 'PORTB.1(x)' to leave RB0 for the interrupt.

SteveB
- 31st October 2012, 19:07
Ross,
if you have declared:

LEDcount CON 8
and
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]

that will translates to an array indexed from 0 to 7, so

OnTime(8) = PRI_HULL_LGHTS_ON_MS
OffTime(8) = PRI_HULL_LGHTS_OFF_MS

Will not result in what you expect.

RossWaddell
- 31st October 2012, 19:16
If the array is 0-based then it should be this:

OnTime(7) = PRI_HULL_LGHTS_ON_MS
OffTime(7) = PRI_HULL_LGHTS_OFF_MS

Oh, man - a real newbie mistake. Thanks Steve!

SteveB
- 31st October 2012, 19:28
Been there, done that, missing hair to prove it!

RossWaddell
- 31st October 2012, 20:38
That was it, all right. Code works as expected - no need to switch back to PORTB.1(x). Now all I have to do is add the interrupt and handler.

Thanks again, Steve - I poured over that code at 12:30 am last night but didn't spot that index error.

RossWaddell
- 3rd January 2013, 17:28
This may not be the right place to ask a technical question about the PIC 16F88 chip, but I thought I'd start here.

I've got the prototype working and have been focusing on the schematic/board layout to finish this project (finally). After getting back some test PCBs for the blinking LEDs, I noticed that the 5 steady on LEDs connected to PORTA.0 via a 2N2222A transistor are not as bright as the other 5 blinking LEDs connected to PORTB.1-6 (note that PORTB.0 is needed for a button and that PORTB.7-8 are used for other blinking lights). I've looked over the datasheet for the PIC 16F88 but I'm a bit confused by the current limitations on page 163 (18.0 Electrical Characteristics). I had hoped that by using a 10k resistor between the 16F88 pins and the base of the 2N2222A that I could sink more current and hence drive more LEDs (PORTA.0 will ultimately need to drive 10 LEDs (5 per side of the model - total of 200mA for PORTA) while each of PORTB.1-6 will drive 2 LEDs (total of 200mA for PORTB). I see that it says 'Maximum current sunk by PORTA/B: 100mA' but I thought that a transistor (maybe a MOSFET?) would work.

Am I dreaming here? Do I need to look for some other solution? I'd hate to move away from this single chip utilizing Darrel's most elegant code.

PBP Code: 6797

Eagle Schematic: 6796

Ioannis
- 3rd January 2013, 19:11
I am not sure that I understood what you want to do. But looking on your schematic, I really do not think that is a good idea to have a pot drive the collector of the 2N2222 and then the transistor drive a load of 200mA. It is not good practice.

If you need to control the power to your load, then a PWM controlling the base of the transistor is sure more appropriate.

Ioannis

RossWaddell
- 3rd January 2013, 19:31
I got the design from another source. The idea was to provide a way to control the brightness of the LEDs without having to have the trim pot inside the model (the PCB will be in a base for easy access).

The trim pots work great for all the other instances, just not the 5 steady-on LEDs. Also, I cannot use PWM in this code at all - it completely throws off the blinking algorithm created by Darrel.

wdmagic
- 4th January 2013, 01:18
I've looked at your schematic, and im not sure what you are doing with those transistors. Are you using them just to turn on and off the leds? is your pots there, to tune the current for the leds?
I use N-Mosfets, instead of transistors, they act more like a switch, transistors turn on more the more voltage/current their fed, but a fet can be turned on fully with a minimal of power. You can drive a FET directly from the chip, no resistor needed. heres what I would do, +5v to Pot Pin 1, Gnd to Pot Pin 3, Pot Pin 2 goes to LED +, LED- goes to FET Drain, FET Source goes to GND, FET Gate goes directly to PIC PIN. this allows to to turn on and off your LEDs with very little to no current/voltage loss on the FET, all FETs will be turned on the same ammount. FETs do not get as warm as regular transistors and can usually handle more current.
The only thing I see a problem with is how many LEDs in parallel you hook up to each POT, You can turn on a LED with 10-25ma, if you have 5 turned on and running at 125ma, that may cause a wattage problem for the POT. But this may not be a problem, It all depends on application and use.
#1 though, you shouldnt be sinking or driving LEDs to the PIC directly, its fine to do this on a breadboard for testing and such, however if your using many leds, its good to throw in a resistor. That notice on how much the chip can sink current wasnt placed there so you could sink current from leds, its a electrical rating for the chip for all outputs, when you hook up a button with a debounce circuit, the chip has to sink som of that current, each pin that has a high input with a resistor that the pic makes low, has to sink current. all these add up, if you start adding LEDs thats alot more it has to handle. It can make the chip work eratic, or fail, it may not happen today or tomarrow. best to just use the pins for data or drive another device to handle the voltage/current.
OH there is also another good reason to use FETs, you can use a seperate voltage source of a much higher voltage, you could drive 12volt headlights on a car using the right FET and they would behave just like the LED, or a Motor, or lots of things. Like I said a FET is like a 1-Direction Switch, either on or off.
Have fun, your project sounds interesting. I did the same type of project a few months back with very little code, not much different than yours. I just now read your thread so I didnt have a chance to post code before you got it working the way you wanted. good luck. :)

RossWaddell
- 5th January 2013, 02:00
The transistors are there so I can drive up to 10 LEDs per PIC pin. The trim pots are to control the LED brightness (via current change).

wdmagic
- 5th January 2013, 02:48
try the N-Mosfets out, I think you will end up liking them better than transistors. if you have a company name and a .com email you can get free samples from Fairchild Semiconductor to test them out. their "BS270" is very good, handles 400ma with a 5ohm resistance, but up to 2 amps with pulses. thats enough without the pulses to drive 25 leds.
below is an example of how to hook them up, very easy and once you get into MOSFETS they tend to end up in tons of your projects :)
http://peu.net/temp/rgbschem.gif
this is not my schematic, but it looks like a circuit i would build.

RossWaddell
- 12th January 2013, 14:50
I have some BS170's so I'll give those a try.

If I still want to implement LED brightness control with the code Darrel came up with earlier in this thread, I wonder if there is a way to combine this with his SPWM_INT.bas module; instead of just setting the port pin high (on), send a SWPM at a user-adjustable duty cycle for the duration of the 'on' period.

wdmagic
- 13th January 2013, 02:09
you can do that easily if you want them all to dim at the same rate using the PICs PWM going into the gate of a seperate FET with its drain tied to all the fets, sources. and its source to ground, this adds a ~.5v voltage drop but you can compensate by adjusting your pots.

I did this on a guys truck, he wanted 2 sets of floorboard lights, operated from a single pushbutton, but he also wanted to control their brightness, it was easy enought to set a 2 bit binary output to 2 FETS, and have them tied to a 3rd FET that was drivin by the PWM pin. and I just used a POT to set the PWM, and a button to select the lights (left, right, both, off).

wdmagic
- 13th January 2013, 02:11
you could also use the pulseout ant treat the FET's like a servo, and adjust duty cycle from 0 to 100,
also have a look at my post about adding PWMs to pics that have none or need more.

RossWaddell
- 26th January 2013, 00:47
you can do that easily if you want them all to dim at the same rate using the PICs PWM going into the gate of a seperate FET with its drain tied to all the fets, sources. and its source to ground, this adds a ~.5v voltage drop but you can compensate by adjusting your pots.

I did this on a guys truck, he wanted 2 sets of floorboard lights, operated from a single pushbutton, but he also wanted to control their brightness, it was easy enought to set a 2 bit binary output to 2 FETS, and have them tied to a 3rd FET that was drivin by the PWM pin. and I just used a POT to set the PWM, and a button to select the lights (left, right, both, off).

But did the LEDs blink? It sounds like your LEDs were steady-on with brightness control. For my application, I need to control the brightness of blinking LEDs. You can see the full code file above (created by Darrel) but I think the relevant portion is:



CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
;CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

;----[Initialize on/off periods & random sequencing (if enabled)]---------------
; (only randomize first 5 blinkies; 6th is strobe which must remaing constant)
; (running lights (7th) are set up separately)
FOR x = 0 to (LEDcount - 2) ; Load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
ENDIF
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

;----[Main Program Loop]----------------------------------------
Main:
' Check if flash mode has changed
IF FlashMode <> Old_FlashMode Then
Old_FlashMode = FlashMode
GOSUB SetNavLtsFlashRates
EndIF

x = (x + 1) // LEDcount
PORTB.1(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

wdmagic
- 26th January 2013, 07:30
yes yours would still blink, because your controlling the primary fet's on off state with individual I/O's, and controlling a single secondary fet that just controls brightness for all blinking leds. say you set the brightness to 50%, then every led that blinks will be at 50% of its brightness, and you can change the brightness even in the middle of a blink.

Heres a basic, very basic program to do similar
this blinks the leds randomly 50% of the time, and you can randomize the brightness too.


'* Date : 1/26/2013 *
'* Version : 1.0 *
'* Notes : 12F683 Christmas Light Sample *
'* : *
'************************************************* ***************
DEFINE ADC_BITS 10 ' A/D number of bits
DEFINE ADC_CLOCK 1 ' Use A/D internal RC clock
DEFINE ADC_SAMPLEUS 50 ' Set sampling time in us

RES1 Var Word ' A/D converter result
RND1 VAR WORD ' Random Result for Light #1
RND2 VAR WORD ' Random Result for Light #2
RND3 VAR WORD ' Random Result for Light #3

TRISIO.0 = 1 ' AN0 is input for Setting Brightness
TRISIO.2 = 0 ' PWM1 is output for Dimming FET

TRISIO.1 = 0 ' LED1 FET
TRISIO.4 = 0 ' LED2 FET
TRISIO.5 = 0 ' LED3 FET

AGAIN:
ADCIN 0, Res1 ' Read Channel 0 data
'Optional replace ACDIN with (RANDOM RES1)
res1 = res1 / 256
HPWM 1, res1, 1000 ' Outputs a PWM @ 1khz w/ 256 Steps

'Insert Code for turning LED's on or off here
RAndom RND1
Random rnd2
Random rnd3
if rnd1 > 32000 then
GPIO.1 = 1
else
GPIO.1 = 0
endif
if rnd1 > 32000 then
GPIO.4 = 1
else
GPIO.4 = 0
endif
if rnd1 > 32000 then
GPIO.5 = 1
else
GPIO.5 = 0
endif

pause 100
goto again
end

RossWaddell
- 29th January 2013, 21:28
I've managed to incorporate both Darrel's blinking routine and his SPWM_INT module into the same project - the idea as it stands now is to feed each blink pin and corresponding PWM (brightness control) pin into 2 FETs connected together (source to drain in a chain). This would mean, though, that for for the 7 blinking LEDs I'll be using 14 pins on this chip and 2 FETs per blinking LED (14 in total). That's better than not having brightness control, for sure, but is there a way to combine this functionality in code?

Here's what I have so far:



' PIC16F88

' Basic blinky code provided by Darrel Taylor
' See forum posting here:
' http://www.picbasic.co.uk/forum/showthread.php?t=17299&p=116934#post116934

#DEFINE USE_LCD_FOR_DEBUG ; comment out for non-debug use

DEFINE OSC 20 ; Set oscillator 20Mhz
DEFINE BLINKYFREQ 100 ; 10mS periods

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _LVP_OFF
#ENDCONFIG

' ************************************************** *************
' Initialization
' ************************************************** *************

;OSCCON = %01100000 ; 4MHz internal osc (not needed when using ext crystal)
pause 100

ANSEL = 0 ; Digital only
TRISA = 0 ; PORTA all output
TRISB = %00000001 ; Make PORTB pin 0 input for flash mode button

OPTION_REG.6 = 1 ; 1=Rising edge (default) or button "PRESS";
; 0=Falling edge or button "RELEASE"

LEDcount CON 7 ; Number of blinking LEDs on PORTA
;(includes running lights, which are
; defined separately below)
' BL1,BL2,BL3,BL4,BL5,STB
OnTimes DATA 50 ,22 ,38 ,75 ,17 , 2 ; default "on" periods for each output
OffTimes DATA 150 ,45 ,38 ,95 ,22 ,48 ; default "off" periods for each output

; (Strobe flashes 2 times per second
; or every 500ms; subtract "on" time)

FlashMode_Default CON 0
EE_FlashMode DATA FlashMode_Default
FlashMode VAR BYTE
READ EE_FlashMode, FlashMode

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE ; randomization used for BL1-5 only
RND VAR WORD : RND = 13864
MIN_ON CON 15 ; Minimum random ON time
MAX_ON CON 115 ; Maximum random ON time
MIN_OFF CON 15 ; Minimum random OFF time
MAX_OFF CON 200 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount-3]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF

CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
;CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
;Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

Old_FlashMode VAR BYTE
PRI_HULL_LGHTS_ON_MS VAR BYTE
PRI_HULL_LGHTS_OFF_MS VAR BYTE

#IFDEF USE_LCD_FOR_DEBUG
LCD_PIN VAR PORTA.4 ' Alias PORTA.4 as "LCD_PIN"
LCD_INST CON 254 ' instruction
LCD_CLR CON 1 ' Clear screen
LCD_L1 CON 128 ' LCD line 1
LCD_L2 CON 192 ' LCD line 2
LCD_BAUD CON 16780 ' Baud rate/mode for ILM-216 2x16 character display
LCD_PACE CON 1 ' Optional pace value
#ENDIF

' ************************************************** *************
' Includes
' ************************************************** *************

INCLUDE "DT_INTS-14.bas" ' DT's base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp3)

INCLUDE "SPWM_INT.bas" ; DT's software PWM module

DEFINE SPWM_FREQ 200 ; SPWM Frequency
DEFINE SPWM_RES 256 ; SPWM Resolution

DutyVars VAR BYTE[3] ; DutyCycle Variables
DutyVar1 VAR DutyVars[0] ; group them in an array for easy access
DutyVar2 VAR DutyVars[1] ; with FOR loops etc.
DutyVar3 VAR DutyVars[2]

ASM
SPWM_LIST macro ; Define Pin's to use for SPWM
SPWM_PIN PORTA, 0, _DutyVar1 ; and the associated DutyCycle variables
SPWM_PIN PORTA, 1, _DutyVar2 ; Notice the underscore before variables
SPWM_PIN PORTA, 2, _DutyVar3
endm
SPWM_INIT SPWM_LIST ; Initialize the Pins
ENDASM

DutyVar1 = 7
DutyVar2 = 25
DutyVar3 = 255

;-- Place a copy of these variables in your Main program -------------------
;-- The compiler will tell you which lines to un-comment --
;-- Do Not un-comment these lines --
;---------------------------------------------------------------------------
;wsave VAR BYTE $20 SYSTEM ' location for W if in bank0
wsave VAR BYTE $70 SYSTEM ' alternate save location for W
' if using $70, comment wsave1-3

' --- IF any of these three lines cause an error ?? ------------------------
' Comment them out to fix the problem ----
' -- Which variables are needed, depends on the Chip you are using --
;wsave1 VAR BYTE $A0 SYSTEM ' location for W if in bank1
;wsave2 VAR BYTE $120 SYSTEM ' location for W if in bank2
;wsave3 VAR BYTE $1A0 SYSTEM ' location for W if in bank3
' --------------------------------------------------------------------------

' ************************************************** *************
' ASM Interrupt Definitions
' ************************************************** *************

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler INT_INT, _Flash_Mode_Btn, PBP, yes
INT_Handler TMR1_INT, SPWMhandler, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

' ************************************************** *************

;----[Initialize on/off periods & random sequencing (if enabled)]---------------
; (only randomize first 5 blinkies; 6th is strobe which must remaing constant)
; (running lights (7th) are set up separately)
FOR x = 0 to (LEDcount - 2) ; Load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
ENDIF
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

Old_FlashMode = FlashMode ; Set initial value for comparator var
GOSUB SetNavLtsFlashRates ; Set up flash rates based on saved "FlashMode" val

' Enable interrupt
INTCON = %10010000 ; Global int enabled, INTE enabled, IOCI enabled,
; INTF flag bit 0 clr, IOCI flag bit 0 clr
INTCON.1 = 0 ; Clear RB0/INT External Interrupt Flag

@ INT_ENABLE INT_INT ; INT/Ext Interrupt
@ INT_ENABLE TMR1_INT ; enable Timer1 interrupts

#IFDEF USE_LCD_FOR_DEBUG
serout2 LCD_PIN, LCD_BAUD, LCD_PACE, [LCD_INST, LCD_CLR] ; clear screen
pause 5
serout2 LCD_PIN, LCD_BAUD, LCD_PACE, ["FlashMode:", DEC FlashMode, " "]
#ENDIF

;----[Main Program Loop]----------------------------------------
Main:
' Check if flash mode has changed
IF FlashMode <> Old_FlashMode Then
Old_FlashMode = FlashMode
GOSUB SetNavLtsFlashRates
EndIF

x = (x + 1) // LEDcount
PORTB.1(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

SetNavLtsFlashRates:
If FlashMode = 0 Then
PRI_HULL_LGHTS_ON_MS = 150
PRI_HULL_LGHTS_OFF_MS = 50
ElseIf FlashMode = 1 Then
PRI_HULL_LGHTS_ON_MS = 50
PRI_HULL_LGHTS_OFF_MS = 150
Else
PRI_HULL_LGHTS_ON_MS = 75
PRI_HULL_LGHTS_OFF_MS = 83
EndIf

OnTime(6) = PRI_HULL_LGHTS_ON_MS
OffTime(6) = PRI_HULL_LGHTS_OFF_MS

#IFDEF USE_LCD_FOR_DEBUG
serout2 LCD_PIN, LCD_BAUD, LCD_PACE, [LCD_INST, LCD_CLR] ; clear screen
pause 5
serout2 LCD_PIN, LCD_BAUD, LCD_PACE, ["FlashMode:", DEC FlashMode, " "]
#ENDIF

RETURN
END

' ************************************************** *************
' [INT - interrupt handler]
' ************************************************** *************
Flash_Mode_Btn:
' Toggle flash mode between the 3 values (0, 1 or 2)
FlashMode = FlashMode + 1
If FlashMode > 2 Then FlashMode = 0

' Save selected running light flash mode
WRITE EE_FlashMode, FlashMode
PAUSE 100

INTCON.1 = 0 ' Clear RB0/INT External Interrupt Flag
@ INT_RETURN

RossWaddell
- 30th January 2013, 00:09
UPDATE: The code compiles just fine, but now PWM output on PORTA.0-2 is observed. Sigh.

RossWaddell
- 31st January 2013, 12:34
UPDATE: The code compiles just fine, but now PWM output on PORTA.0-2 is observed. Sigh.

I meant to say **no** PWM output on pins PORTA.0-2 is observed; the pins are held low and the LEDs don't blink with the daisy chain FET approach.

Looks like I'll have to use 2 PICS: one for the blink stuff developed by Darrel, and one to control the brightness via PWM.

RossWaddell
- 26th March 2014, 01:39
(Reviving an old thread)

I now need to simplify this a bit and put this on a 8-pin PIC. I need 5 blinkies so I'd like to use a 12F629 (knowing GP3/MCLR is input only, I'll use GP0-GP5 and if nothing happens on GP3 then that's fine).

The code won't compile because of this part:



;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1


These registers exist on a 16F690 (and others) but not 12F629. I had earlier tried a 12F683 (my go-to PIC) but the .inc file for it already has a define for CCPR1 so I'd have to comment out that line in the .inc file but that makes me a wee bit uncomfortable.

Is there a way to make this work on a 12F690 or 12F683? If not, is there another 8-pin PIC I could use? I need to be able to find SMT packages as the space where the PCB is going is extremely tight (I've never soldered SMTs before so that should be fun).

Here's the whole code:



'************************************************* ***************
'* Name : Nacelle_Blinking_Lights_5_12F629_4Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'************************************************* ***************


' For production version of this code, update config fuses to
' enable code protect on

' Basic blinky code provided by Darrel Taylor
' See forum posting here:
' http://www.picbasic.co.uk/forum/showthread.php?t=17299&p=116934#post116934

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' GP5 -> pin 2 -> R4 -> BL4
' GP4 -> pin 3 -> R5 -> BL5
' GP3 -> pin 4 -> MCLR (input only, 'dummy' BL6)
' GP2 -> pin 5 -> R1 -> BL1
' GP1 -> pin 6 -> R2 -> BL2
' GP0 -> pin 7 -> R3 -> BL3

' ************************************************** *************
' Initialization
' ************************************************** *************

DEFINE OSC 4 ' Set oscillator 4Mhz

DEFINE BLINKYFREQ 100 ' 10mS periods

CMCON = %0000111 'Turn off comparators
TRISIO = %00000000 'Make all GPIO pins output

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BODEN_ON & _CP_OFF & _CPD_OFF
#ENDCONFIG

LEDcount CON 6 ; Number of blinking LEDs
; (includes 'fummy' LED on GP3/MCLR pin,
; rates defined separately below)

' BL3,BL2,BL1,BL6,BL5,BL4
' --- --- --- --- --- ---
' default "on" periods for each output
OnTimes DATA 50 ,82 ,50 ,33 ,133, 2
' default "off" periods for each output
OffTimes DATA 150 ,45 ,50 ,33 ,22 ,48

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 33 ; Minimum random ON time
MAX_ON CON 100 ; Maximum random ON time
MIN_OFF CON 33 ; Minimum random OFF time
MAX_OFF CON 100 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 500, WORD 2000, WORD 2000
#ENDIF

CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

' ************************************************** *************

;----[Initialize on/off periods & random sequencing (if enabled)]---------------
FOR x = 0 to (LEDcount) ; Load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
IF x < 5 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
ENDIF
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

Main:
x = (x + 1) // LEDcount
GPIO.0(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

Darrel Taylor
- 26th March 2014, 03:12
The 12F629 doesn't have a CCP module.
You'll be better off with the 12F683.

Don't comment the CCPR1 line in the .inc file. Comment the one in the blinky code.

CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L

Amoque
- 26th March 2014, 03:34
You might also, if I follow your intent, use an AND gate to combine PWM and I/O into one PWM modulated blinking output.

RossWaddell
- 26th March 2014, 11:38
The 12F629 doesn't have a CCP module.
You'll be better off with the 12F683.

Don't comment the CCPR1 line in the .inc file. Comment the one in the blinky code.

CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L


Thanks Darrel!

RossWaddell
- 26th March 2014, 19:34
You might also, if I follow your intent, use an AND gate to combine PWM and I/O into one PWM modulated blinking output.
Presumably I'd need 5 AND gates for the 5 outputs, right? and then connect the other input of each AND gate to a PWM from another chip?

RossWaddell
- 27th March 2014, 03:01
The 12F629 doesn't have a CCP module.
You'll be better off with the 12F683.

Don't comment the CCPR1 line in the .inc file. Comment the one in the blinky code.

CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L


I converted the code to use a 12F683 PIC but the LEDs come on and then stay on (although not all 5; some stay off). Is it because of the input-only GP4/MCLR?



' Basic blinky code provided by Darrel Taylor
' See forum posting here:
' http://www.picbasic.co.uk/forum/showthread.php?t=17299&p=116934#post116934

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' GP5 -> pin 2 -> R4 -> BL4
' GP4 -> pin 3 -> R5 -> BL5
' GP3 -> pin 4 -> MCLR (input only, 'dummy' BL6)
' GP2 -> pin 5 -> R1 -> BL1
' GP1 -> pin 6 -> R2 -> BL2
' GP0 -> pin 7 -> R3 -> BL3

' Initialization
' ************************************************** *************

DEFINE OSC 8 ' Set oscillator 8Mhz

DEFINE BLINKYFREQ 100 ' 10mS periods

OSCCON = %0111
CMCON0 = %0000111 ' Turn off comparators
ANSEL.0 = 0 ' Digital only
ANSEL.1 = 0 ' Digital only
ANSEL.2 = 0 ' Digital only
ANSEL.3 = 0 ' Digital only
ADCON0.0 = 0 ' ADC is disabled
TRISIO = %00000000 ' Make all GPIO pins output

' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite

#CONFIG
__config _INTRC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOD_ON & _CP_OFF & _CPD_OFF
#ENDCONFIG

LEDcount CON 6 ; Number of blinking LEDs
; (includes 'fummy' LED on GP3/MCLR pin,
; rates defined separately below)

' BL3,BL2,BL1,BL6,BL5,BL4
' --- --- --- --- --- ---
' default "on" periods for each output
OnTimes DATA 50 ,82 ,50 ,33 ,133, 2
' default "off" periods for each output
OffTimes DATA 150 ,45 ,50 ,33 ,22 ,48

;----------------------------------------------------------------
#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence
#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 33 ; Minimum random ON time
MAX_ON CON 100 ; Maximum random ON time
MIN_OFF CON 33 ; Minimum random OFF time
MAX_OFF CON 100 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 500, WORD 2000, WORD 2000
#ENDIF

CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
;CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer1 VAR WORD EXT : @Timer1 = TMR1L
CCPIF VAR PIR1.2

LoopLED VAR BYTE[LEDcount]
OnTime VAR BYTE[LEDcount]
OffTime VAR BYTE[LEDcount]
x VAR BYTE

' ************************************************** *************

;----[Initialize on/off periods & random sequencing (if enabled)]---------------
FOR x = 0 to (LEDcount) ; Load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;-- setup CCP1 and Start Timer1 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer1 = 0 ; clear Timer1
T1CON.0 = 1 ; start Timer1

Main:
x = (x + 1) // LEDcount
GPIO.0(x) = !!(LoopLED(x) < OnTime(x))
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF
IF x != (LEDcount - 1) THEN Main

Waiting: IF !CCPIF THEN Waiting
CCPIF = 0
GOTO Main

Darrel Taylor
- 27th March 2014, 03:53
On the 12F683, the CCP1IF bit is PIR1.5.
Why they have to change things all the time ... I don't know.

CCPIF VAR PIR1.5


Both the OSCCON and CMCON0 values don't have enough digits.
added: CMCON0 won't matter cause it's still 7, but to keep things consistent ...


OSCCON = %01110000 ' 8Mhz
CMCON0 = %00000111 ' Turn off comparators


Not sure how this got changed, it should be ...


FOR x = 0 to (LEDcount - 1) ; Load the periods from EEPROM



http://support.melabs.com/DT/Blinky5_683.gif

Amoque
- 27th March 2014, 12:36
Again, not wholly sure what your trying to do... I have trouble following someone else's code (even my own sometimes), but a PWM output tied to one input of each (yes, 5) "AND" gate, the other input to each I/O. Toggling I/O then controls modulated output. I am thinking of IR communications where a serial data stream and 38K PWM are "ANDed" for IR transmission.

RossWaddell
- 27th March 2014, 14:29
On the 12F683, the CCP1IF bit is PIR1.5.
Why they have to change things all the time ... I don't know.

CCPIF VAR PIR1.5


Both the OSCCON and CMCON0 values don't have enough digits.
added: CMCON0 won't matter cause it's still 7, but to keep things consistent ...


OSCCON = %01110000 ' 8Mhz
CMCON0 = %00000111 ' Turn off comparators


Not sure how this got changed, it should be ...


FOR x = 0 to (LEDcount - 1) ; Load the periods from EEPROM



http://support.melabs.com/DT/Blinky5_683.gif

Thank you SOOOO much, Darrel!

BTW, what software is that you are using to test out the code?

RossWaddell
- 27th March 2014, 14:33
Again, not wholly sure what your trying to do... I have trouble following someone else's code (even my own sometimes), but a PWM output tied to one input of each (yes, 5) "AND" gate, the other input to each I/O. Toggling I/O then controls modulated output. I am thinking of IR communications where a serial data stream and 38K PWM are "ANDed" for IR transmission.
I was initially intending to make the brightness of the LEDs controllable via PWM, but now the chips are moving off the main board to another PCB that will be buried deep within my model where they will only have +5V & GND wires. These boards are too small to add any other components (even SMTs) so I'll just have to breadboard them with various series-limiting resistors to get the right brightness.

Your suggestion is something I will investigate for the rest of my circuit where I do intend to provide brightness control on the main board. Right now, I'm daisy-chaining 2 NPNs which someone on SparkFun suggested but I haven't breadboarded it yet to see if it works.

Darrel Taylor
- 27th March 2014, 17:54
BTW, what software is that you are using to test out the code?
It's Proteus Design Suite from Labcenter Electronics.


I was initially intending to make the brightness of the LEDs controllable via PWM, ... so I'll just have to breadboard them with various series-limiting resistors to get the right brightness.
That can be done in software using MIBAM.
The blinky's timer will have to be changed, but it's doable.

RossWaddell
- 28th March 2014, 00:50
Spent a very happy afternoon reading up on MIBAM - can't wait to try it out (is there a later version of the .pbp code or is the v1.0 you posted on the melbas page the one I should use?).

In preparation for that, I converted the now-workiing code to use Timer2. I think that since Timer2 is 8-bit (versus 16-bit for Timer1) that I don't need to worry about using the Least Significant register as you did with Timer1 but can just use TMR2:



CCPR1val CON EXT : @CCPR1val = (OSC*1000000/4)/ BLINKYFREQ
;CCPR1 VAR WORD EXT : @CCPR1 = CCPR1L
Timer2 VAR WORD EXT : @Timer2 = TMR2
CCPIF VAR PIR1.5


And then this to turn it on:



;-- setup CCP1 and Start Timer2 --
CCPR1 = CCPR1val ; set compare value
CCP1CON = %00001011 ; compare mode, special event
Timer2 = 0 ; clear Timer2
T2CON.0 = 1 ; start Timer2


Is that it?

RossWaddell
- 28th March 2014, 03:12
Or maybe not. The PIC12F683 data sheet seems to indicate that the Compare mode uses only Timer1, and if that's needed for MIBAM then I don't think I can use that on a PIC12F683 with this blinky code.

Darrel Taylor
- 28th March 2014, 04:57
One little obstacle and ... it can't be done???:eek:

The CCP with Capture mode was just a simple way to get a wide range of constant periods without interrupts/reloading timers etc.

Since it seems that you are only using the original 10mS periods, it doesn't really need that "Wide Range".
10mS periods can be generated with another Timer, and you don't need the CCP module.

In this example, I'm using Timer0 ...
MIBAM uses Timer1.

The EEPROM data labeled Brightness controls the intensity of each LED.
I have 3 LED's with 330 ohms, and 2 LED's with 100 ohms. The existing values equalized their brightness (to my eyes).

;{PIC=12F683}

#CONFIG
__config _INTOSCIO & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOD_ON & _IESO_OFF & _FCMEN_OFF
#ENDCONFIG


DEFINE OSC 8
OSCCON = %01110000 ; 8MHz internal osc

ANSEL = 0 ; All Digital
CMCON0 = 7
OPTION_REG = %00000110 ; Timer0 prescaler 1:128
TMR0IF VAR INTCON.2 ; Timer0 Overflow bit
TRISIO = 0 ; all OUTPUT

;----[ MIBAM Setup ]--------------------------------------------------------
wsave var byte $70 SYSTEM ' Alternate save location for W
ssave VAR BYTE BANK0 SYSTEM ' location for STATUS register
psave VAR BYTE BANK0 SYSTEM ' location for PCLATH register

BAM_COUNT CON 5 ; How many BAM Pins are used?
;DEFINE BAM_INFO 1
INCLUDE "MIBAM.pbp" ; Mirror Image BAM module

Bright VAR BYTE
Br0 VAR Bright[0]
Br1 VAR Bright[1]
Br2 VAR Bright[2]
Br4 VAR Bright[3]
Br5 VAR Bright[4]

ASM
BAM_LIST macro ; Define PIN's to use for BAM
BAM_PIN (GPIO,0, Br0) ; and the associated Duty variables
BAM_PIN (GPIO,1, Br1)
BAM_PIN (GPIO,2, Br2)
BAM_PIN (GPIO,4, Br4)
BAM_PIN (GPIO,5, Br5)
endm
BAM_INIT BAM_LIST ; Initialize the Pins
ENDASM

;----[Setup Blinky parameters]-----------------------------------
DEFINE BLINKYFREQ 100 ; 10mS periods
LEDcount CON 5 ; Number of LEDs on the PORT
OnTimes DATA 50, 22, 38,75, 5 ; default periods for each Output
OffTimes DATA 150, 45, 38,95,34
[B]Brightness DATA 150,150,150,80,80

#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence

#IFDEF USE_RANDOM_SEQUENCE

RND VAR WORD : RND = 13864
MIN_ON CON 50 ; Minimum random ON time
MAX_ON CON 500 ; Maximum random ON time
MIN_OFF CON 50 ; Minimum random OFF time
MAX_OFF CON 500 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF


;----[Variables used only by Blinky]-----------------------------
LoopLED VAR WORD[LEDcount]
OnTime VAR WORD[LEDcount]
OffTime VAR WORD[LEDcount]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to LEDcount - 1 ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE

READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF

NEXT X


;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, Bright(x)
ELSE
Bright(x) = 0
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE

RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main

And yes, I posted a MIBAM version 1.1 several years ago ... but it seems to be gone, or I just can't find it..
Here's the latest version.

RossWaddell
- 28th March 2014, 13:56
I must sound like a broken record, but thanks again Darrel. I greatly appreciate you taking the time to help me with my project.

RossWaddell
- 29th March 2014, 14:58
As the purpose of this project is to create 5 blinking LEDs that simulate old 1960's Christmas tree lights, I've added the following code to fade in/out the LED:



IsOn VAR BYTE[LEDCount]
i VAR BYTE

FOR x = 0 to (LEDCount - 1) ; set initial LED state to OFF
IsOn(x) = 0
NEXT x

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
IF IsOn(x) = 1 THEN
READ Brightness + x, Bright(x)
ELSE
IsOn(x) = 1
; Fade up LED to set brightness
FOR i = 0 to Bright(x) Step 1
Bright(x) = i
PAUSE 20
NEXT i
ENDIF
ELSE
IF IsOn(x) = 1 THEN
IsOn(x) = 0
; Fade out LED
FOR i = Bright(x) to 0 Step -1
Bright(x) = i
PAUSE 20
NEXT i
ELSE
Bright(x) = 0
ENDIF
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main


I'll program a 12F683 this afternoon and see what it looks like.

RossWaddell
- 29th March 2014, 15:49
I should've known - the PAUSE command will interfere with the on/off timings. I'll try a lower PAUSE value but then the 'ramping up/down' effect I'm trying to achieve won't really be visible.

Darrel Taylor
- 29th March 2014, 16:09
The timing of the program is done by counting 10mS periods.
So the entire loop can't take more than 10mS.

When the LED is supposed to be ON, just add a certain amount to the dutycycle each time through the loop until it reaches the set maximum.
The amount you add will determine the speed of the fade.

Subtract some when it is supposed to be OFF, until it reaches 0 or underflows.

Don't add any pauses.

RossWaddell
- 29th March 2014, 21:48
Do you mean something like this (I'm sure there's more elegant code, but ...):



temp VAR WORD

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, temp
IF Bright(x) < temp THEN
Bright(x) = Bright(x) + 2
ELSE
Bright(x) = temp
ENDIF
ELSE
IF Bright(x) > 0 THEN
Bright(x) = Bright(x) - 2
ELSE
Bright(x) = 0
ENDIF
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main

Darrel Taylor
- 29th March 2014, 23:09
That's Perfect!!

May take more than 2 for the Speed, but well done!

Darrel Taylor
- 29th March 2014, 23:15
In preparation for the next step I had written this almost identical code.
There are a few differences that might be of benefit.


MaxBright VAR BYTE
FadeSpeed CON 20

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, MaxBright
IF Bright(x) <= (MaxBright - FadeSpeed) THEN
Bright(x) = Bright(x) + FadeSpeed
ELSE
Bright(x) = Maxbright
ENDIF
ELSE
IF Bright(x) >= FadeSpeed THEN
Bright(x) = Bright(x) - FadeSpeed
ELSE
Bright(x) = 0
ENDIF
ENDIF

RossWaddell
- 30th March 2014, 18:03
Looks like 6 is the magic number.

RossWaddell
- 18th July 2015, 02:09
(If this is bad etiquette to re-open a 1-yr old thread then I'll start a new one)

Well, what was working (I think) last year isn't this year as I prepare to move this to production with a SMT version of the 12F683. The blinking still works but the brightness control doesn't - no matter what the values are for Brightness all 5 LEDs appear to not change (they're still way too bright).



'************************************************* ***************
'* Name : Nacelle_Blinking_Lights_12F683_8Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2015 *
'* : All Rights Reserved *
'* Date : 06/30/2015 *
'* Version : 3.0 *
'* Notes : Blinking nacelle engine lights (TOS Enterprise) *
'* *
'************************************************* ***************


' Nacelle engine lights
' =====================
' (a) The colours used were: red, blue, green, amber and pink, all standard
' colours they had for common Christmas lights of the time.
' (b) They were all C7 Christmas lights (too big for a 1/350). 12 by my guess.
' (c) Amber was steady (5 in a star pattern), all the rest blinked.


' For production version of this code, update config fuses to
' enable code protect (CP_ON)

' Basic blinky code provided by Darrel Taylor
' See forum posting here:
' http://www.picbasic.co.uk/forum/showthread.php?t=17299&p=116934#post116934

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' GP5 -> pin 2 -> R4 -> BL4
' GP4 -> pin 3 -> R5 -> BL5
' GP3 -> pin 4 -> MCLR (input only, 'dummy' BL6)
' GP2 -> pin 5 -> R1 -> BL1
' GP1 -> pin 6 -> R2 -> BL2
' GP0 -> pin 7 -> R3 -> BL3

' ************************************************** *************
' Initialization
' ************************************************** *************

' If not using the MCLR pin as in input, there is no need to add an external resistor to tie it
' to VDD if the PIC has an internal pull-up. See http://www.picbasic.co.uk/forum/archive/index.php/t-872.html

' MCLR=OFF means that MCLR is handled INTERNALLY and that pin can be used for I/O (Input actually).
' Whatever you do to the pin in this instance will have no bearing on the PIC running or not. It will
' be just another INPUT pin. You could leave it floating if it was unused.


#CONFIG
__config _INTOSCIO & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOD_ON & _IESO_OFF & _FCMEN_OFF
#ENDCONFIG


DEFINE OSC 8

OSCCON = %01110000 ; 8MHz internal osc
ANSEL = 0 ; All Digital
CMCON0 = 7
TRISIO = 0 ; all OUTPUT
OPTION_REG = %00000110 ; Timer0 prescaler 1:128

TMR0IF VAR INTCON.2 ; Timer0 Overflow bit

;----[ MIBAM Setup ]---------------------------------------------
wsave var byte $70 SYSTEM ' Alternate save location for W
ssave VAR BYTE BANK0 SYSTEM ' location for STATUS register
psave VAR BYTE BANK0 SYSTEM ' location for PCLATH register

BAM_COUNT CON 5 ; How many BAM Pins are used?

;DEFINE BAM_INFO 1
INCLUDE "MIBAM.pbp" ; Mirror Image BAM module

Bright VAR BYTE[BAM_COUNT]
Br0 VAR Bright[0]
Br1 VAR Bright[1]
Br2 VAR Bright[2]
Br4 VAR Bright[3]
Br5 VAR Bright[4]

ASM
BAM_LIST macro ; Define PIN's to use for BAM
BAM_PIN (GPIO,0, Br0) ; and the associated Duty variables
BAM_PIN (GPIO,1, Br1)
BAM_PIN (GPIO,2, Br2)
BAM_PIN (GPIO,4, Br4)
BAM_PIN (GPIO,5, Br5)
endm
BAM_INIT BAM_LIST ; Initialize the Pins
ENDASM

;----[Setup Blinky parameters]-----------------------------------
DEFINE BLINKYFREQ 100 ; 10mS periods

LEDcount CON 5 ; Number of LEDs on the PORT

; BL3,BL2,BL1,BL5,BL4 ; PCB indicators
OnTimes DATA 50, 22, 50, 75, 17 ; default periods for each Output
OffTimes DATA 150, 45, 50, 95, 22

; Blinky on/off info from older code
; ====================================
' BL3,BL2,BL1,BL5,BL4
;OnTimes DATA 50 ,22 ,50 ,75 ,17; default "on" periods for each output
;OffTimes DATA 150 ,45 ,50 ,95 ,22; default "off" periods for each output
;#IFDEF USE_RANDOM_SEQUENCE ; randomization
; RND VAR WORD : RND = 13864
; MIN_ON CON 220 ; Minimum random ON time
; MAX_ON CON 333 ; Maximum random ON time
; MIN_OFF CON 33 ; Minimum random OFF time
; MAX_OFF CON 275 ; Maximum random OFF time
; RandPeriod VAR WORD[LEDcount]
; RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
;#ENDIF

; Adjust values below for the 5 blinkies re: brightness
Brightness DATA 5,5,5,5,5

#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence

#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 50 ; Minimum random ON time
MAX_ON CON 500 ; Maximum random ON time
MIN_OFF CON 50 ; Minimum random OFF time
MAX_OFF CON 500 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF

;----[Variables used only by Blinky]-----------------------------
LoopLED VAR WORD[LEDcount]
OnTime VAR WORD[LEDcount]
OffTime VAR WORD[LEDcount]
x VAR BYTE
temp VAR WORD
speed CON 6

;----[Initialize]------------------------------------------------
FOR x = 0 to (LEDcount - 1) ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;----[Main Program Loop]-----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, temp
IF Bright(x) < temp THEN
Bright(x) = Bright(x) + speed
ELSE
Bright(x) = temp
ENDIF
ELSE
IF Bright(x) > 0 THEN
Bright(x) = Bright(x) - speed
ELSE
Bright(x) = 0
ENDIF
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main


As you can see, I haven't implemented Darrel's most recent suggestion (probably because a year ago this was working OK).

Any ideas?

RossWaddell
- 18th July 2015, 16:18
Went back through my versioning of the code and found that the previous one works just fine - I must've made some tweaks in v3 that screwed things up. I'll need to go through them line-by-line to see where the discrepancy is (if only for my own benefit).

Here's the working code:



'************************************************* ***************
'* Name : Nacelle_Blinking_Lights_12F683_8Mhz_Int.pbp *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2014 *
'* : All Rights Reserved *
'* Date : 03/25/2014 *
'* Version : 2.0 *
'* Notes : Blinking nacelle engine lights (TOS Enterprise) *
'* : *
'************************************************* ***************


' Nacelle engine lights
' =====================
' (a) The colours used were: red, blue, green, amber and pink, all standard colours
' they had for common Christmas lights of the time.
' (b) They were all Christmas lights (C7, too big for a 1/350). 12 by my guess.
' (c) Amber was steady (5 in a star pattern), all the rest blinked.


' For production version of this code, update config fuses to
' enable code protect (CP_ON)

' Basic blinky code provided by Darrel Taylor
' See forum posting here:
' http://www.picbasic.co.uk/forum/showthread.php?t=17299&p=116934#post116934

' ************************************************** *************
' Pin Connections
' ************************************************** *************

' GP5 -> pin 2 -> R4 -> BL4
' GP4 -> pin 3 -> R5 -> BL5
' GP3 -> pin 4 -> MCLR (input only, 'dummy' BL6)
' GP2 -> pin 5 -> R1 -> BL1
' GP1 -> pin 6 -> R2 -> BL2
' GP0 -> pin 7 -> R3 -> BL3

' ************************************************** *************
' Initialization
' ************************************************** *************

#CONFIG
__config _INTOSCIO & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOD_ON & _IESO_OFF & _FCMEN_OFF
#ENDCONFIG


DEFINE OSC 8

OSCCON = %01110000 ; 8MHz internal osc
ANSEL = 0 ; All Digital
CMCON0 = 7
TRISIO = 0 ; all OUTPUT
OPTION_REG = %00000110 ; Timer0 prescaler 1:128

TMR0IF VAR INTCON.2 ; Timer0 Overflow bit

;----[ MIBAM Setup ]--------------------------------------------------------
wsave var byte $70 SYSTEM ' Alternate save location for W
ssave VAR BYTE BANK0 SYSTEM ' location for STATUS register
psave VAR BYTE BANK0 SYSTEM ' location for PCLATH register

BAM_COUNT CON 5 ; How many BAM Pins are used?

;DEFINE BAM_INFO 1
INCLUDE "MIBAM.pbp" ; Mirror Image BAM module

Bright VAR BYTE[BAM_COUNT]
Br0 VAR Bright[0]
Br1 VAR Bright[1]
Br2 VAR Bright[2]
Br4 VAR Bright[3]
Br5 VAR Bright[4]

ASM
BAM_LIST macro ; Define PIN's to use for BAM
BAM_PIN (GPIO,0, Br0) ; and the associated Duty variables
BAM_PIN (GPIO,1, Br1)
BAM_PIN (GPIO,2, Br2)
BAM_PIN (GPIO,4, Br4)
BAM_PIN (GPIO,5, Br5)
endm
BAM_INIT BAM_LIST ; Initialize the Pins
ENDASM

;----[Setup Blinky parameters]-----------------------------------
DEFINE BLINKYFREQ 100 ; 10mS periods
LEDcount CON 5 ; Number of LEDs on the PORT

; BL3,BL2,BL1,BL5,BL4 ; PCB indicators
OnTimes DATA 50, 22, 38, 75, 5 ; default periods for each Output
OffTimes DATA 150, 45, 38, 95, 34
Brightness DATA 255,10,10, 10, 255

#DEFINE USE_RANDOM_SEQUENCE ; comment for contiuous Sequence

#IFDEF USE_RANDOM_SEQUENCE
RND VAR WORD : RND = 13864
MIN_ON CON 50 ; Minimum random ON time
MAX_ON CON 500 ; Maximum random ON time
MIN_OFF CON 50 ; Minimum random OFF time
MAX_OFF CON 500 ; Maximum random OFF time
RandPeriod VAR WORD[LEDcount]
RandPeriods DATA WORD 1000, WORD 1250, WORD 1500, WORD 1750, WORD 2000
#ENDIF


;----[Variables used only by Blinky]-----------------------------
LoopLED VAR WORD[LEDcount]
OnTime VAR WORD[LEDcount]
OffTime VAR WORD[LEDcount]
x VAR BYTE

;----[Initialize]------------------------------------------------
FOR x = 0 to LEDcount - 1 ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;----[Main Program Loop]----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, Bright(x)
ELSE
Bright(x) = 0
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main

RossWaddell
- 18th July 2015, 16:56
After going through the two different versions I remembered that the last thing I wanted to implement was varying the brightness of each LED to ramp up/off to the set BRIGHTNESS level. This would simulate incandescent bulbs by 'fading' up the LED (and also fade off).

This is the suspect code:



temp VAR WORD
speed CON 6

;----[Initialize]------------------------------------------------
FOR x = 0 to (LEDcount - 1) ; load the periods from EEPROM
READ OnTimes+x, OnTime(x)
READ OffTimes+x, OffTime(x)
#IFDEF USE_RANDOM_SEQUENCE
READ RandPeriods+(x<<1), WORD RandPeriod(x)
#ENDIF
NEXT X

;----[Main Program Loop]-----------------------------------------
Main:
x = (x + 1) // LEDcount
IF LoopLED(x) < OnTime(x) THEN
READ Brightness + x, temp
IF Bright(x) < temp THEN
Bright(x) = Bright(x) + speed
ELSE
Bright(x) = temp
ENDIF
ELSE
IF Bright(x) > 0 THEN
Bright(x) = Bright(x) - speed
ELSE
Bright(x) = 0
ENDIF
ENDIF
LoopLED(x) = (LoopLED(x) + 1) // (OnTime(x) + OffTime(x))
#IFDEF USE_RANDOM_SEQUENCE
RandPeriod(x) = RandPeriod(x) - 1
IF RandPeriod(x) = 0 THEN
READ RandPeriods+(x<<1), WORD RandPeriod(x)
RANDOM RND
OnTime(x) = (MAX_ON - MIN_ON)* RND.HighByte / 255 + MIN_ON
OffTime(x)= (MAX_OFF - MIN_OFF)* RND.LowByte / 255 + MIN_OFF
ENDIF
#ENDIF

IF x != (LEDcount - 1) THEN Main

Waiting: IF !TMR0IF THEN Waiting
TMR0 = 99
TMR0IF = 0
GOTO Main