;=============================================================================== ; Title: Soldering Station Display Driver ; ; Author: Rob Jansen, Copyright (c) 2021..2021, all rights reserved. ; ; Revisions ; --------- ; 2021-04-10 : Initial version. ; ; Description: Driver for the 7-segment displays. This driver does not mutiplex ; the indiviual 7-segment displays but the individual segments. This ; simplifies the electronic design. Timer 4 is used for multiplexing. ; Procedures are provided for writing some messages on the display. ; Sources: - ; ; --------------------------------- Pins --------------------------------------- ; Display control const bit DISPLAY_ON = LOW const bit DISPLAY_OFF = HIGH ; The segment displays are connected to PORTC. Set to low before making it output. alias SEGMENT_PORT is PORTC SEGMENT_PORT = 0 PORTC_Direction = OUTPUT ; Pins that activate the various 7-segment displays. Make them LOW before making ; them output alias display_control_units is Pin_A0 display_control_units = DISPLAY_OFF Pin_A0_Direction = OUTPUT alias display_control_tens is Pin_B6 display_control_tens = DISPLAY_OFF Pin_B6_Direction = OUTPUT alias display_control_hundreds is Pin_B7 display_control_hundreds = DISPLAY_OFF Pin_B7_Direction = OUTPUT ; -------------------------- Constant declarations ----------------------------- ; Conversion table to translate a byte to 7-segment display. The segments ; are activated by an active high signal, assigned as follows to a byte: ; msb to lsb = pgfe dcba. Bit 7 (the decimal point) is not used. const byte DIGIT_TO_7_SEGMENT[] = { 0b_0011_1111, ; 0 = fedcba 0b_0000_0110, ; 1 = cb 0b_0101_1011, ; 2 = gedba 0b_0100_1111, ; 3 = gdcba 0b_0110_0110, ; 4 = gfcb 0b_0110_1101, ; 5 = gfdca --> Also used for indicating an error 0b_0111_1101, ; 6 = gfedca 0b_0000_0111, ; 7 = cba 0b_0111_1111, ; 8 = gfedcba 0b_0110_1111, ; 9 = gfdcba 0b_0111_0111, ; A = gfecba --> Used for indicating calibration 0b_0111_1100, ; b = gfedc 0b_0011_1001, ; C = feda --> Used for indicating calibration 0b_0101_1110, ; d = gedcb 0b_0111_1001, ; E = gfeda --> Used for indicating an error 0b_0111_0001, ; F = gfea 0b_0100_0000, ; - = g --> 16: Used for indicating an error 0b_0111_0110, ; H = gfecb --> 17: Used for indicating an error 0b_0011_1000 ; L = fed --> 18: Used for indicating calibration } ; Multiplexer constants. const byte MUX_UNITS = 0 const byte MUX_TENS = 1 const byte MUX_HUNDREDS = 2 ; Conversion table to translate a segment to the correct display output. const byte NUMBER_OF_SEGMENTS = 8 const byte SEGMENT_TO_DISPLAY[NUMBER_OF_SEGMENTS] = { 0b_0000_0001, ; a 0b_0000_0010, ; b 0b_0000_0100, ; c 0b_0000_1000, ; d 0b_0001_0000, ; e 0b_0010_0000, ; f 0b_0100_0000, ; g 0b_1000_0000 ; dp } ; -------------------------- Variable declarations ----------------------------- ; For the multiplexing of the segments we can activate the same segment on ; each 7-segment display at the same time (if it is active). So we need to ; store the segment data based on the digit we want to display. var byte segment_data[3] ; Holds the data for the 7-segment displays var byte display_state ; Holds the state of the display multiplexer. var byte segment_counter ; 0=a, 1=b, 2=c, .... 7=p var bit decimal_point_on ; Used to display something special. ; ------------------------- Functions and Procedures---------------------------- ; Initializes the display driver. procedure display_init() Is ; First initalize some global variables. display_state = MUX_UNITS display_control_units = DISPLAY_OFF display_control_tens = DISPLAY_OFF display_control_hundreds = DISPLAY_OFF decimal_point_on = FALSE ; All segments off. segment_data[0] = 16 ; Units '-' segment_data[1] = 16 ; Tens '-' segment_data[2] = 16 ; Hundreds '-' segment_counter = 0 ; We will use Timer4 as input clock as to have a low interrupt frequency. ; Since this displays are multiplexed the frequency should not be too low. ; Take 60 Hz as refresh rate. Because we need to multiplex 8 segments ; (including dp) and we do it per display (total 3) we choose a frequency ; of 60 * 8 * 3 = 1440 Hz. T4CON_TMR4ON = FALSE ; Timer 4 off T4CON_T4OUTPS = 0b0000 ; Postscaler is 1:1 T4CON_T4CKPS = 0b11 ; Prescaler divide by 64 ; Register PR4 holds the Timer Period using the following formula: ; Period = (PR4 + 1) * 4 * Tosc * Timer4 prescale value * postscaling ; where Tosc = 1/Fosc and Fosc = 32.000.000 Hz ; Setting the prescaler at 16 and a PR4 of 85 gives: ; (85 + 1) * 4 * 1/32.000.000 * 64 * 1 = 688 us Period Cycle (about 1653 Hz) PR4 = 85 ; PR4 compare value of Timer 4. TMR4 = 0 PIE3_TMR4IE = TRUE ; Enable Timer 4 interrupt. PIR3_TMR4IF = FALSE ; Clear Timer 4 interrupt flag. T4CON_TMR4ON = TRUE ; Start Timer 4 end procedure ; Interrupt procedure to multiplex the display and update them with the ; value stored in the global variable 'segment_data'. procedure display_mutiplexer is pragma interrupt var byte display_segment if PIR3_TMR4IF & PIE3_TMR4IE then PIR3_TMR4IF = FALSE ; Update the next segment for all displays. Disable displays ; and set new segment. display_control_units = DISPLAY_OFF display_control_tens = DISPLAY_OFF display_control_hundreds = DISPLAY_OFF SEGMENT_PORT = SEGMENT_TO_DISPLAY[segment_counter] ; Multiplex the LED displays. Order is: Units, Tens, Hundreds. case display_State of MUX_UNITS: block ; First check for decimal point. if decimal_point_on then display_segment = segment_data[0] | 0b_1000_0000 else display_segment = segment_data[0] end if if (display_segment & SEGMENT_TO_DISPLAY[segment_counter]) > 0 then display_control_units = DISPLAY_ON end if display_state = MUX_TENS end block MUX_TENS: block ; First check for decimal point. if decimal_point_on then display_segment = segment_data[1] | 0b_1000_0000 else display_segment = segment_data[1] end if if (display_segment & SEGMENT_TO_DISPLAY[segment_counter]) > 0 then display_control_tens = DISPLAY_ON end if display_state = MUX_HUNDREDS end block MUX_HUNDREDS: block ; First check for decimal point. if decimal_point_on then display_segment = segment_data[2] | 0b_1000_0000 else display_segment = segment_data[2] end if if (display_segment & SEGMENT_TO_DISPLAY[segment_counter]) > 0 then display_control_hundreds = DISPLAY_ON end if display_state = MUX_UNITS ; Next segment. segment_counter = (segment_counter + 1) % NUMBER_OF_SEGMENTS end block end case end if end procedure ; Set the given temperature on the display procedure display_temperature(word in temperature) is var byte units var byte tens var byte hundreds var bit display_enable ; Convert word to the segment data, unit, tens, hundreds. ; We calculate this first as to minimize the time we disable ; the interrupg (see below) units = DIGIT_TO_7_SEGMENT[byte(temperature % 10)] tens = DIGIT_TO_7_SEGMENT[byte((temperature / 10) % 10)] hundreds = DIGIT_TO_7_SEGMENT[byte(temperature / 100)] ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = units segment_data[1] = tens segment_data[2] = hundreds PIE3_TMR4IE = display_enable end procedure ; Display sensor error 'E-S'. procedure display_sensor_error() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[5] ; "S" segment_data[1] = DIGIT_TO_7_SEGMENT[16] ; "-" segment_data[2] = DIGIT_TO_7_SEGMENT[14] ; "E" PIE3_TMR4IE = display_enable end procedure ; Display heater error 'E-H' procedure display_heater_error() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[17] ; "H" segment_data[1] = DIGIT_TO_7_SEGMENT[16] ; "-" segment_data[2] = DIGIT_TO_7_SEGMENT[14] ; "E" PIE3_TMR4IE = display_enable end procedure ; Display calibrate error 'E-C' procedure display_calibrate_error() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[12] ; "C" segment_data[1] = DIGIT_TO_7_SEGMENT[16] ; "-" segment_data[2] = DIGIT_TO_7_SEGMENT[14] ; "E" PIE3_TMR4IE = display_enable end procedure ; Display calibration message 'CAL' procedure display_calibrate_message() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[18] ; "L" segment_data[1] = DIGIT_TO_7_SEGMENT[10] ; "A" segment_data[2] = DIGIT_TO_7_SEGMENT[12] ; "C" PIE3_TMR4IE = display_enable end procedure ; Display low calibrate message 'C-L' procedure display_calibrate_message_low() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[18] ; "L" segment_data[1] = DIGIT_TO_7_SEGMENT[16] ; "-" segment_data[2] = DIGIT_TO_7_SEGMENT[12] ; "C" PIE3_TMR4IE = display_enable end procedure ; Display high calibrate message 'C-H' procedure display_calibrate_message_high() is var bit display_enable ; While updating disable the display update to prevent strange ; numbers on the 7-segment display. display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE segment_data[0] = DIGIT_TO_7_SEGMENT[17] ; "H" segment_data[1] = DIGIT_TO_7_SEGMENT[16] ; "-" segment_data[2] = DIGIT_TO_7_SEGMENT[12] ; "C" PIE3_TMR4IE = display_enable end procedure ; Switch on all decimal points. procedure display_decimal_points_on() is var bit display_enable display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE decimal_point_on = TRUE PIE3_TMR4IE = display_enable end procedure ; Switch off all decimal points. procedure display_decimal_points_off() is var bit display_enable display_enable = PIE3_TMR4IE PIE3_TMR4IE = FALSE decimal_point_on = FALSE PIE3_TMR4IE = display_enable end procedure