;=============================================================================== ; Title: RF Thermostat Display ; ; Author: Rob Jansen, Copyright (c) 2017 .. 2017, all rights reserved. ; ; Revisions ; --------- ; 2017-04-01 : Initial version copied from Star SPI. ; ; Description: The SPI interface is used to control and multiplex two 7-segment ; displays using an external shift register. ; Sources: - ; ; ================== Constant and variable declarations ======================= ; Specify connected SPI IO-pins for controlling the Shift Register that controls ; the 7-Segment displays. Alias Shift_Clock is pin_C0 ; Shift register clock, via SPIO pin 10. pin_C0_direction = output Alias Shift_Store is pin_C1 ; Shift Store, moving bits to SR output pin 9. pin_C1_direction = output Alias Shift_Data is pin_C2 ; Shift register Data, via SPIO pin 8. pin_C2_direction = output Alias Shift_ResetN is pin_C3 ; Shift reset. Active Low pin 7. pin_C3_direction = output ; 7-Segment displays have a common anode which is powered by an active low. Alias Shift_CAU_N is pin_C4 ; Common Anode Units Segment. Active Low pin 6. pin_C4_direction = output Alias Shift_CAT_N is pin_C5 ; Common Anode Tens Segement. Active Low pin 5. pin_C5_direction = output Alias Shift_CAF_N is Pin_A1 ; Common Anode Fraction Segement. Active Low pin 12 Pin_A1_Direction = OUTPUT ; Control Constants for the 7-segment Display. Const Bit DISPLAY_CONTROL_ON = FALSE Const Bit DISPLAY_CONTROL_OFF = TRUE ; Constants used for multiplexing the LED displays. Const Byte MUX_FRACTION = 0 Const Byte MUX_UNITS = 1 Const Byte MUX_TENS = 2 ; Conversion table to translate a byte to 7-segment display. The segments ; are activated by an active low signal, assigned as follows to a byte: ; msb to lsb = Pgfe dcba where P is the decimal point. Const Byte DISPLAY_OPTIONS = 17 Const Byte DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS] = {0b_1100_0000, ; 0 = fedcba 0b_1111_1001, ; 1 = cb 0b_1010_0100, ; 2 = gedba 0b_1011_0000, ; 3 = gdcba 0b_1001_1001, ; 4 = gfcb 0b_1001_0010, ; 5 = gfdca 0b_1000_0010, ; 6 = gfedca 0b_1111_1000, ; 7 = cba 0b_1000_0000, ; 8 = gfedcba 0b_1001_0000, ; 9 = gfdcba 0b_1000_1000, ; A = gfecba 0b_1000_0011, ; b = gfedc 0b_1100_0110, ; C = feda 0b_1010_0001, ; d = gedcb 0b_1000_0110, ; E = gfeda 0b_1000_1110, ; F = gfea 0b_1011_1111} ; - = g, used to indicate no display value. ; Display constant and variable declarations. Const Word DISPLAY_BLINK_TIME = 250 ; Equals one second blink time Var Word Display_Blinker ; Counter to create display blink. Var Byte Display_State ; State machine variable for Display Var Byte Display_Segment_T ; Holds the Shift Register data for the tens Segment. Var Byte Display_Segment_U ; Holds the Shift Register data for the units Segment. Var Byte Display_Segment_F ; Holds the Shift Register data for the fraction Segment. Var Bit Display_Blink ; If TRUE will blink the 7 Segment Displays. Var Bit Display_DP_T_On ; If TRUE decimal point of tens display will be on. Var Bit Display_DP_U_On ; If TRUE decimal point of units display will be on. Var Bit Display_DP_F_On ; If TRUE decimal point of fraction display will be on. Var Bit Display_Control ; Holds the value to set the display on or off. ; ========================= Functions and Procedures ========================== Procedure Display_Init Is ; Initialize the SPI interface and the external Shift Registers that control Shift_ResetN = FALSE ; Clear Shift Registers. Shift_Store = FALSE ; Reset line for storing shift data . Shift_CAT_N = DISPLAY_CONTROL_OFF ; Disable Common Anode high Segment. Shift_CAU_N = DISPLAY_CONTROL_OFF ; Disable Common Anode low Segment. ; Initialize display to '-' Display_Segment_T = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] Display_Segment_U = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] Display_Segment_F = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] ; We will use Timer2 as input clock as to have a low interrupt frequency. ; Since this displays are multiplexed the frequency should not be too low. ; We choose a SPI clock frequencey of about 2000 Hz so an interrupt frequency ; of 250 Hz. Timer 2 needs to create a frequency of 4000 Hz or 250 us. T2CON_TMR2ON = FALSE -- Timer 2 off T2CON_T2OUTPS = 0b0000 -- Postscaler is 1:11 T2CON_T2CKPS = 0b11 -- Prescaler divide by 64 ; Register PR2 holds the Timer Period using the following formula: ; Period = (PR2 + 1) * 4 * Tosc * Timer2 prescale value * postscaling ; where Tosc = 1/Fosc and Fosc = 32.000.000 Hz ; Setting the prescaler at 16 and a PR2 of 30 gives: ; (30 + 1) * 4 * 1/32.000.000 * 64 = 256 us Period Cycle (about 3096 Hz Hz) PR2 = 30 -- PR2 compare value of Timer 2 TMR2 = 0 -- Reset Timer ; Set CCP2CON to have a software interrupt, the P2M and DC2B bits have no ; use then. ; CCP2CON_CCP2M = 0b1010 ; We do not need a Timer 2 interrupt PIE1_TMR2IE = FALSE ; Roll-over interrupt must be enabled otherwise Timer 2 does not work INTCON_PEIE = TRUE ; Start Timer 2 (and so SPI multiplexer) T2CON_TMR2ON = TRUE ; Intialize the SPI for controlling the Shift Registers. ; Set Serial Connection Control Register. SSP1CON2 and SSP1CON3 are not ; relevant since used for IIC or reading via SPI what we do not use. SSP1CON1_SSPEN = FALSE ; Disable SPI while configuring SSP1CON1_SSPM = 0b0011 ; Use Timer2 / 2 clock ; Set clock mode of SPI so that Data is clocked in SR when clock line goes up ; that is CKP = 0 and CKE = 1. SSP1CON1_CKP = FALSE SSPSTAT_CKE = TRUE ; Set global display variables. Display_State = MUX_FRACTION ; Start at the beginning of the state machine. Display_Blink = FALSE ; No blinking is active. Display_Blinker = 0 ; Reset blink timer. ; Turn the display on. Display_Control = DISPLAY_CONTROL_ON Display_DP_T_On = FALSE ; Decimal point for Tens is off. Display_DP_U_On = FALSE ; Decimal point for units is off. Display_DP_F_On = FALSE ; Decimal point for fraction is off. ; Reset flags, release shift register and start SPI. PIR1_SSPIF = FALSE ; Clear SPI interrupt flag. Shift_ResetN = TRUE ; Enable Shift Registers, disable reset. SSP1CON1_SSPEN = TRUE ; Enable SPI. PIE1_SSP1IE = TRUE ; Enable Serial Interrupt SSP1BUF = 0xFF ; 0xFF is display off, generate SPI interrupt. End Procedure ; Display_Init Procedure Display_Write(Byte In Tens, Byte In Units, Byte In Fraction) Is ; Writes the given values to the display. The parameters must be within 0x0 to ; 0xF otherwise the display will be set to 'X'. If (Tens < DISPLAY_OPTIONS) Then Display_Segment_T = DIGIT_TO_7_SEGMENT[Tens] Else Display_Segment_T = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] End If ; Tens If (Units < DISPLAY_OPTIONS) Then Display_Segment_U = DIGIT_TO_7_SEGMENT[Units] Else Display_Segment_U = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] End If ; Units If (Fraction < DISPLAY_OPTIONS) Then Display_Segment_F = DIGIT_TO_7_SEGMENT[Fraction] Else Display_Segment_F = DIGIT_TO_7_SEGMENT[DISPLAY_OPTIONS - 1] End If ; Fraction End Procedure ; Display_Write Procedure Switch_Display_On Is ; Switch LED Display On. Display_Control = DISPLAY_CONTROL_ON End Procedure ; Switch_Display_On Procedure Switch_Display_Off Is ; Switch LED Display Off. Display_Control = DISPLAY_CONTROL_OFF End Procedure ; Switch_Display_Off Function Display_Is_On Return Bit Is ; Returns TRUE if the display is on. If the display is blinking that is seen ; as the fact that the display is off. Return (Display_Control == DISPLAY_CONTROL_ON) | Display_Blink End Function ; Display_Is_On Function Display_Is_Off Return Bit Is ; Returns TRUE if the display is off. The display must not be blinking. Return (Display_Control == DISPLAY_CONTROL_OFF) & !Display_Blink End Function ; Display_Is_Off Procedure Display_Blink_On Is ; Switch on the blink mode of the display. Display_Blinker = DISPLAY_BLINK Display_Blink = TRUE End Procedure ; Display_Blink_On Procedure Display_Blink_Off Is ; Switch off the blink mode of the display. Display_Blink = FALSE End Procedure ; Display_Blink_Off Procedure Display_Tens_Point_On Is ; Switch on the decimal point of the tens display. Display_DP_T_On = TRUE End Procedure ; Display_Tens_Point_On Procedure Display_Tens_Point_Off Is ; Switch off the decimal point of the tens display. Display_DP_T_On = FALSE End Procedure ; Display_Tens_Point_Off Procedure Display_Units_Point_On Is ; Switch on the decimal point of the units display. Display_DP_U_On = TRUE End Procedure ; Display_Units_Point_On Procedure Display_Units_Point_Off Is ; Switch off the decimal point of the unitts display. Display_DP_U_On = FALSE End Procedure ; Display_Unitts_Point_Off Procedure Display_Fraction_Point_On Is ; Switch on the decimal point of the fraction display. Display_DP_F_On = TRUE End Procedure ; Display_Fraction_Point_On Procedure Display_Fraction_Point_Off Is ; Switch off the decimal point of the fraction display. Display_DP_F_On = FALSE End Procedure ; Display_Fraction_Point_Off Procedure Display_Interrupt Is Pragma interrupt ; Interrupt occured, check if SPI interrupt occured. If so then we multiplex ; the data for the three LED displays. If PIR1_SSPIF Then ; First we check if we need to toggle the display on or off. If Display_Blink Then If (Display_Blinker == 0) Then Display_Blinker = DISPLAY_BLINK_TIME Display_Control = !Display_Control ; On <--> Off Else Display_Blinker = Display_Blinker - 1 End If End If Case Display_State Of ; Multiplex the LED displays. Note that each state moves the data to the ; LED display of the previous LED display and prepares the data for itself. ; Order is: Fraction, Units, Tens. MUX_FRACTION: Block ; Disable the Units display, store the value of Tens display and ; prepare the data for the Fraction display. Shift_CAU_N = DISPLAY_CONTROL_OFF ; De-activate Units display. Shift_Store = TRUE ; Output Tens data to Display. Shift_CAT_N = Display_Control ; Activate Tens display if set. ; Send Fraction data to Shift Register. If Display_DP_F_On Then SSP1BUF = Display_Segment_F & 0b0111_1111 ; Switch on decimal point. Else SSP1BUF = Display_Segment_F ; Write value of fraction display. End If Display_State = MUX_UNITS Shift_Store = FALSE End Block MUX_UNITS: Block ; Disable the Tens display, store the value of Fraction display and ; prepare the data for the Units display. Shift_CAT_N = DISPLAY_CONTROL_OFF ; De-activate Tens display. Shift_Store = TRUE ; Output Fraction to Display. Shift_CAF_N = Display_Control ; Activate Fraction display if set. ; Send Unit data to Shift Register. If Display_DP_U_On Then SSP1BUF = Display_Segment_U & 0b0111_1111 ; Switch on decimal point. Else SSP1BUF = Display_Segment_U ; Write value of units display. End If Display_State = MUX_TENS Shift_Store = FALSE End Block MUX_TENS: Block ; Disable the Fraction display, store the value of Units display and ; prepare the data for the Tens display. Shift_CAF_N = DISPLAY_CONTROL_OFF ; De-activate Fraction display. Shift_Store = TRUE ; Output Units to Display. Shift_CAU_N = Display_Control ; Activate Units display if set. ; Send Tens data to Shift Register. If Display_DP_T_On Then SSP1BUF = Display_Segment_T & 0b0111_1111 ; Switch on decimal point. Else SSP1BUF = Display_Segment_T ; Write value of Tens display End If Display_State = MUX_FRACTION Shift_Store = FALSE End Block End Case ; Clear interrupt flag PIR1_SSPIF = FALSE End If End Procedure ; Display_Interrupt