# How would you do it: Turn Signal & Brake Light Logic

Status
Not open for further replies.

#### Mike - K8LH

##### Well-Known Member
I was surprised that the logic is subtly complex and I was wondering how someone else might tackle the problem?

In my example (below) I'm using XC8 and coding for a 14-pin 16F1503. I connected active low <left>, <right>, and <brake> switches to RA0, RA1, and RA2, respectively, and I connected active high left and right turn signal LEDs to RA4 and RA5, respectively.

I'm using a simple timed loop running at a 25-millisecond switch "debounce" interval and I derive a non-blocking "flash timer" function from within that loop to flash (toggle) an active turn signal LED at a 250-millisecond flash rate interval. I start the "flash timer" function on detecting a turn signal switch "new press" state and I turn off the "flash timer" function on detecting the debounced switch "release" state at the end of the flash rate interval. Turn signal "on" and "off" latency is the 250-millisecond flash rate interval.

The "turns" variable contains either an RA4 (left) or an RA5 (right) turn signal LED bit mask when either turn signal is active and provides a simple mechanism for toggling the active turn signal LED at the end of each 250-millisecond flash rate interval. The brake light logic also relies on the "turns" bit mask variable to determine which LEDs it needs to turn on or off.

I'd love to see some alternative programming methods/algorithms for this application.

Cheerful regards, Mike

C:
/*****************************************************************
*    File: Basic_Turn_Signals.c                                 *
*  Author: Mike McLaren, K8LH                                   *
*    Date: 15-May-2017                                          *
*                                                               *
*  16F1503 Simple Turn Signal & Brake Light Exercise            *
*                                                               *
*     IDE: MPLAB 8.92                                           *
*    Lang: XC8 v1.35                                            *
*                                                               *
*****************************************************************/

#include <xc.h>

//  config 1
#pragma config FOSC = INTOSC  //
#pragma config WDTE = OFF     //
#pragma config PWRTE = ON     //
#pragma config MCLRE = OFF    //
#pragma config CP = OFF       //
#pragma config BOREN = ON     //
#pragma config CLKOUTEN = OFF //

//  config 2
#pragma config WRT = OFF      //
#pragma config STVREN = ON    //
#pragma config BORV = LO      //
#pragma config LPBOR = OFF    //
#pragma config LVP = OFF      //

#define _XTAL_FREQ 500000

/*                                                               *
*  variables                                                    *
*                                                               */
unsigned char turns = 0;      // active turn signal mask
unsigned char flash = 10;     // flash rate timer
unsigned char swnew = 0;      // switch state filter
unsigned char swlat = 0;      // switch state latch

/*****************************************************************
*  main                                                         *
*                                                               */
void main()
{                             //
ANSELA = 0;                 // adc off, digital I/O
ANSELC = 0;                 // adc off, digital I/O
TRISA = 0b00000111;         // RA2(brake) RA1(right) RA0(left)
WPUA = 0b00000111;          // weak pull-ups on RA2..RA0
OPTION_REG &= 0b01111111;   // global weak pull-up enable
OSCCON = 0b01010000;        // intosc = 500kHZ

while(1)                    //
{ __delay_ms(25);           // 25-ms debounce/loop interval
/*                                                             *
*  K8LH Parallel Switch State Logic (and "new press" filter)  *
*                                                             *
*  swnew  ___---___---_____    invert active lo switches      *
*  swlat  ____---___---____    switch state latch             *
*  swnew  ___-__-__-__-____    changes, press or release      *
*  swnew  ___-_____-_______    filter out 'release' bits      *
*                                                             */
swnew = ~PORTA;           // invert active lo switches
swnew &= 7;               // on the RA2..RA0 pins only
swnew ^= swlat;           // changes, press or release
swlat ^= swnew;           // update switch state latch
swnew &= swlat;           // filter out 'release' bits
/*                                                             *
*  check for Right or Left turn signal "new press" state      *
*                                                             */
if(swnew & 1)             // if Left Sw (RA0) "new press"
turns = 0b00010000;     // active Lt signal (RA4 mask)

if(swnew & 2)             // if Right Sw (RA1) "new press"
turns = 0b00100000;     // active Rt signal (RA5 mask)
/*                                                             *
*  toggle active turn signal LED at the flash rate interval   *
*                                                             */
if(turns)                 // if either signal active
{ if(--flash == 0)        // if flash rate interval
{ flash = 10;           // reset flash timer (250-msecs)
if(swlat & 3)         // if switch still "pressed"
LATA ^= turns;      // toggle turn signal LED
else                  // turn signal switch "released"
turns = 0;          // turn off turn signal
}                       //
}                         //

/*                                                             *
*  brake logic (applied during each 25-msec loop interval)    *
*                                                             *
*  brake switch on & neither turn signal -> light both LEDs   *
*  brake switch on & active left signal -> light right LED    *
*  brake switch on & active right signal -> light left LED    *
*  brake switch off -> only active turn signal LED lighted    *
*                                                             */
if(swlat & 4)             // if Brake Sw (RA2) "pressed"
LATA |= (0b00110000 ^ turns);
else
LATA = ((LATA & 0b11001111) | (LATA & turns));
}                           // end while(1)
}                             // end main()

Last edited:

#### be80be

##### Well-Known Member
That code looks really cool i'm working on learning XC8

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Over this side of the pond, the two have to be completely separate.. So if I did it here it would be a shed load easier!!

On another note!! I made an LED brake light unit and had issues with the bulb monitoring system...If it isn't LED's already you have to shunt the Brake and Turn supply so no nasties appear on the dash..

Good code.. As always!

#### Mike - K8LH

##### Well-Known Member
Thanks, guys.

Ian, I believe that's the case here, too (separate turn signal & brake lights on autos).

I should mention this experiment is a prelude to a sequential turn signal + brake lights for my bicycle. I just wanted to work out some of the logic in this simple experiment.

Cheerful regards, Mike

#### BobW

##### Active Member
Only question I have is why do you need to debounce your inputs?

#### DirtyLude

##### Well-Known Member
Does the switches power the controller? Otherwise I'm not certain how your debounce works. If your switch lands on the 25ms loop start, no debounce is happening.

I'm a fan of more descriptive variable names, switch_latch, switch_read, turn_signal_state, brake_signal_state. Shortforming everything just makes it more difficult to read. You can also turn your hardcoded values into defines. e.g. Define LEFT_SIGNAL to 0b00010000, and use the definition, that way if you change the pins anytime you can change it in the defines.

Personally I would have put the light flashing in timer routines and used a state machine in the main loop to read the signals and set the flash status. I don't think it would have been any better than your code for the purpose, other than catching the switch to wait for the debounce, but it would have freed up the controller from that 25ms wait and do nothing routine if you had more functionality to handle.

#### Mike - K8LH

##### Well-Known Member
Ganssle conflates switch bounce and noise. If noise isn't a problem, you don't need a low pass filter, you simply need to sample the switches at some 'debounce' interval. If the switch is bouncing when it's sampled it should be stable by the next sample. That is, the switch will be sampled at the correct state anywhere from 0t < sample < 2t, where t is the sample interval, after it's pressed or released.

In your example where the switch has been pressed immediately before the sample interval, the bouncing switch may be read as either 'open' or 'closed' and so it may or may not trigger a state change in the switch state logic during the current sample, but the switch should be stable at the next sample interval. In this case the switch may be sampled correctly (by luck) during the current sample interval or certainly during the subsequent sample interval.

Personally, I find long descriptive variable names (and large indentation) contribute to long instruction lines which make it difficult (for me) to keep track of the logic and flow. Shorter lines with meaningful comments work better (for me).

An interrupt based timer would work just as well in this application and I think a state machine example would be wonderful. If you find some spare time please consider posting an example.

Cheerful regards, Mike

Last edited:

#### DirtyLude

##### Well-Known Member
Interesting, yes, in Jack's case he's using a timer or systick to check debounce rather than halting the whole program. If timing is not an issue than this is absolutely fine. I had to go through your latching logic to figure out what was going on and it is quite elegant. The whole program is very purpose built code. I'm assuming you were mostly an assembler coder as there is a lot of code optimization here at the expense of expandability or reusability. It's definitely super compact, though. Not certain how much manual optimization you needed to keep it under the 2k limit with unoptimizing XC8.

As long as the variable naming conventions work for you, that's fine. In any coding shop there's no way those what get by any standard. At least a separator between your abbreviations would be useful. In my last company it was all lowercase underscore separator, in my current company it's camelcase, which I find annoying.

I'll see if I can make up something as an experiment. I have been spoiled by other uC's with systick timers that have little to no setup and it's easy to implement background counters.

#### Mike - K8LH

##### Well-Known Member
Hi Mark (DirtyLude):

Yes, I started out with assembly language which is where that parallel switch state logic originated;
Code:
;
;  K8LH Parallel Switch State Logic (and "new press" filter)
;
;   wreg  ____---____-----_____   inverted active lo sample
;  swold  _____---____-----____   switch state latch
;   wreg  ____-__-___-____-____   changes, press or release
;   wreg  ____-______-_________   filter out 'release' bits
;
comf    PORTA,W         ; sample active lo switches       |00
andlw   1<<sw1|1<<sw2   ; sw1 (RA0) & sw2 (RA1) only      |00
xorwf   swold,W         ; changes, press or release       |00
xorwf   swold,F         ; update switch state latch       |00
andwf   swold,W         ; filter out 'release' bits       |00
;
;  task a 32-ms 2000-Hz beep on any 'new press'
;
skpz                    ; new press? no, skip, else       |00
bsf     beep,0          ; task a 'new press' beep         |00
;
;  toggle 'new press' flag bits for main
;
xorwf   flags,F         ; toggle flag bits for main       |00
I also discovered that when using a rotary encoder (with detents) as a front panel control the switch state logic will detect exactly one "new press" for both "A" and "B" decoder switches as the decoder moves through all four switch phases between detents. Simply lock onto an "A" or "B" switch "new press" and use the debounced state of the opposite switch to determine direction.
Code:
 /*                                                                 *
*  K8LH parallel switch state logic (and "new press" filter)      *
*                                                                 *
*  swnew  ___---_____---__________   invert active lo switches    *
*  swlat  ____---_____---_________   switch state latch           *
*  swnew  ___-__-____-__-_________   changes, press or release    *
*  swnew  ___-_______-____________   filter out 'release' bits    *
*                                                                 */
swnew = ~PORTA;                 // invert active lo switches
swnew ^= swlat;                 // changes, press or release
swlat ^= swnew;                 // update switch state latch
swnew &= swlat;                 // filter out 'release' bits
/*                                                                 *
*  if encoder B "new press" use debounced A state as direction    *
*                                                                 */
if(swnew & 0b00100000)          // if encoder B "new press"
{ if(swlat & 0b00010000)        // if encoder A state = 1
flags |= inc_sw_mask;       // pseudo INC sw "new press"
else                          //
flags |= dec_sw_mask;       // pseudo DEC sw "new press"
}                               //
Just one of many many different ways to code for a rotary encoder but I thought it was kind of neat.

Sounds like you have quite a bit of professional experience, Mark. I'm very envious.

Remember to have fun.

Cheerful regards, Mike

Last edited:

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
Just one of many many different ways to code for a rotary encoder but I thought it was kind of neat
Kindly give it to MrDeb.... he's struggling a bit!!!!

I too like state machines... They are neat and usually need very little maintenance...

#### Mike - K8LH

##### Well-Known Member
I believe MrDeb uses BASIC so not much help there... And the switch he's using isn't really a rotary encoder like the ones I'm using...

Last edited:

#### Ian Rogers

##### User Extraordinaire
Forum Supporter
I believe MrDeb uses BASIC so not much help there...
True.. I sometimes wish I could teach him the fundamentals of asm, it would be a boon, but hey ho....

#### DirtyLude

##### Well-Known Member
Sounds like you have quite a bit of professional experience, Mark. I'm very envious.
I do boring programming in proprietary languages for HR/Payroll/Time and Attendance. Never done anything embedded professionally. I enjoy doing programming that is more challenging as a hobby, but definitely my main focus is reusability now.

Still like to write this with a state machine and a timer, but not certain when I can get around to it. Maybe this weekend.

Status
Not open for further replies.