Search code examples
cpicseven-segment-display

Pic16f684 & dual seven seg display program: random number generator in C


I'm trying to convert this code to be able to generate random numbers 1-56. I would understand, but because the program has to switch back and forth to be able to show both digits, I'm confused.

I know in some respect, I can use rand() to choose.

I will eventually use this code in junction with an lcd screen to display the numbers as well as the dual seven segment display, but for now, just trying to figure out to program a PIC16F684 to generate the random number on the dua84l seven seg display

Below is the circuit and code. Not included in the diagram, but in the code is RA3 & RA4 have buttons that clear the display or generate the next digit.

The program supplied displays 00-0xFF as a button gets pushed. It may be easier to rewrite the program, but I just dont understand it

Circuit

#include <xc.h>
/*
*
*
* 6/30/2020
/******************************************/
/* ------------------------------------------- */
/* Software/Hardware Interface: */
/* ------------------------------------------- */
/* */
/* Select Right Digit using >> RA0 */
/* Select Left Digit using >> RA1 */
/* */
/* Segment a >> RA5 */
/* Segment b >> RC5 */
/* Segment c >> RC4 */
/* Segment d >> RC3 */
/* Segment e >> RC2 */
/* Segment f >> RC1 */
/* Segment g >> RC0 */
/* ------------------------------------------- */


void PORTA_init(void)
{
    PORTA = 0;          //  All PORTA Pins are low
    CMCON0 = 7;         //  Turn off Comparators
    ANSEL = 0;          //  Turn off ADC
    //TRISA = 0b001111;   //  RA4 and 5 are outputs; RA0,1,2, and 3 are input
    return;
}
/******** END OF PORTA_init ****************************/
void delay_routine(void)
{
    int i, j;
        for (i = 0; i<2000; i++);
        for (j = 0; j <2000;j++);
    return;
}
/******** END OF delay_20ms *************************/
// CONFIG --- Configuration Word --- START
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = OFF
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
// CONFIG --- Configuration Word --- END

int i, j;
int DisplayValue, DisplayLED;

const char LEDDigit[] = {
   0b0000001, // "0"
   0b1001111, // "1"
   0b0010010, // "2"
   0b0000110, // "3"
   0b1001100, // "4"
   0b0100100, // "5"
   0b0100000, // "6"
   0b0001111, // "7"
   0b0000000, // "8"
   0b0001100, // "9"
   0b0001000, // "A"
   0b0000000, // "b"
   0b0110001, // "C"
   0b0000001, // "d"
   0b0110000, // "E"
   0b0111000
}; // "F"  


main()
{
 PORTA = 0;
 PORTC = 0;
 CMCON0 = 7; // Turn off Comparators 
 ANSEL = 0; // Turn off ADC 
 TRISA = 0b011101; // RA5 and RA1 are outputs
 TRISC = 0b000000; 
         
 DisplayValue = 0; // Start Displaying at 0x00 
 DisplayLED = 0; // Display the 1s first
 while(1 == 1) // Loop Forever 
 {
        if (0 == DisplayLED) // True, then display right digit
        { 
            RA5 = LEDDigit[DisplayValue & 0x0F] >> 6;
                // Clears display bits 4 - 7 of DisplayValue,
                // then selects bit 7 of LEDDigit
            PORTC = LEDDigit[DisplayValue & 0x0F] & 0x03F;
                // clears display bits 4 - 7 of DisplayValue,
                // then selects bits 0 - 6 of LEDDigit 
        }
        else 
        { 
            RA5 = LEDDigit[(DisplayValue >> 4) & 0x0F] >> 6;
            PORTC = LEDDigit[(DisplayValue >> 4) & 0x0F] & 0x03F;
        } // 
        TRISA = TRISA ^ 0b000011; // Swap Left/Right (RA0 and RA1)
        PORTA = PORTA & 0b111100; // Make Sure Bits are Low 
        DisplayLED = DisplayLED ^ 1; // Other Digit Next

        NOP(); // Used for 10 ms Timing 
        for (i = 0; i < 30; i++);//10ms Delay Loop 
        NOP(); // Used for 10 ms Timing

        if (RA3 == 0) 
        {
            delay_routine();
            DisplayValue++; // Increment the Counter 
            delay_routine();
            
            } //
        if (RA4 == 0)
        {
            delay_routine();
            DisplayValue=0;
            delay_routine();
            
        }
    } 
}


Solution

  • @tjpplay,

    The code you posted has some subtle issues and a failure to alternate the digit driver enables.

    Your method to detect a button press disrupts the digit multiplexer timing and causes flicker.

    With the digit drivers connected to the PGC and PGD pins used for In-Circuit-Serial-Programmer(ICSP) makes in circuit programming difficult. In-Circuit-Debug(ICD) will not work with this implementation.

    The code avoids the Read-Modify-Write(RMW) trap for new players by only using 8-bit writes to the PORTA and PORTC registers.

    Using syntax that allows the C compiler to do single bit sets and clears can have a RMW issue for controllers like the PIC16F684 especially when driving LED directly.

    I think that this code may work with your hardware:

    /*
     * File:   main.c
     * Author: dan1138
     * Target: PIC16F684
     * Compiler: XC8 v2.20
     * IDE: MPLABX v5.25
     * 
     * Description:
     *
     * Created on July 21, 2020, 3:45 PM
     * 
     *                            PIC16F684
     *                  +------------:_:------------+
     *         GND -> 1 : VDD                   VSS : 14 <- 5v0
     * SEG_a_DRIVE <> 2 : RA5/T1CKI     PGD/AN0/RA0 : 13 <> DIGIT_DRIVE_2
     *         SW2 <> 3 : RA4/AN3       PGC/AN1/RA1 : 12 <> DIGIT_DRIVE_1
     *         SW1 -> 4 : RA3/VPP           AN2/RA2 : 11 <> 
     * SEG_b_DRIVE <> 5 : RC5/CPP1          AN4/RC0 : 10 <> SEG_g_DRIVE
     * SEG_c_DRIVE <> 6 : RC4/C2OUT         AN5/RC1 : 9  <> SEG_f_DRIVE
     * SEG_d_DRIVE <> 7 : RC3/AN7           AN6 RC2 : 8  <> SEG_e_DRIVE
     *                  +---------------------------:
     *                             DIP-14
     */
    
    // CONFIG --- Configuration Word --- START
    #pragma config FOSC = INTOSCIO
    #pragma config WDTE = OFF
    #pragma config PWRTE = OFF
    #pragma config MCLRE = OFF
    #pragma config CP = OFF
    #pragma config CPD = OFF
    #pragma config BOREN = OFF
    #pragma config IESO = OFF
    #pragma config FCMEN = OFF
    // CONFIG --- Configuration Word --- END
    
    #include <xc.h>
    #include <stdlib.h>
    
    /* Oscillator frequency we will select with the OSCCON register */
    #define _XTAL_FREQ (4000000ul)
    /*
     * Segment locations
     * of an LED display
     *      ---a---
     *     :       :
     *     f       b
     *     :       :
     *      ---g---
     *     :       :
     *     e       c
     *     :       :
     *      ---d---
     */
    const unsigned char LEDDigit[] = {
    //     abcdefg, Segment on = 0
        0b00000001, // "0"
        0b01001111, // "1"
        0b00010010, // "2"
        0b00000110, // "3"
        0b01001100, // "4"
        0b00100100, // "5"
        0b00100000, // "6"
        0b00001111, // "7"
        0b00000000, // "8"
        0b00001100, // "9"
        0b00001000, // "A"
        0b01100000, // "b"
        0b00110001, // "C"
        0b01000010, // "d"
        0b00110000, // "E"
        0b00111000  // "F"  
    }; 
    
    
    void main(void) 
    {
        unsigned char DisplayValue, DisplayLED, DigitSegments;
        unsigned char LoopCount;
        
        PORTA = 0;
        PORTC = 0;
        CMCON0 = 7;                 // Turn off Comparators 
        ANSEL = 0;                  // Turn off ADC 
        
        __delay_ms(500);            // wait for ICD before making PGC and PGD outputs;
        TRISA = 0b011100;           // RA5, RA1, RA0 are outputs
        TRISC = 0b000000; 
        OPTION_REGbits.nRAPU = 0;   // Enable weak pull-up on PORTA
        WPUA = 0;                   // Turn off all pull-ups
        WPUAbits.WPUA4 = 1;         // Turn on RA4 pull-up
        
             
        DisplayValue = 0;           // Start Displaying at 0x00 
        DisplayLED = 0;             // Display the 1s first
        LoopCount = 0;
        
        for(;;)
        {
            PORTC = 0xFF;   // turn off all segment drivers
            PORTA = 0xFF;   // and digit drivers
            if (1 == (DisplayLED & 1))
            {
                DigitSegments = LEDDigit[(DisplayValue >> 4) & 0x0F];
                if(DigitSegments & 0b1000000)
                {
                    PORTA = 0b111110;   // turn on Digit driver 2
                } 
                else 
                {
                    PORTA = 0b011110;   // turn on Digit driver 2 and SEG_a_DRIVER
                }
            }
            else
            {
                DigitSegments = LEDDigit[DisplayValue & 0x0F];
                if(DigitSegments & 0b1000000)
                {
                    PORTA = 0b111101;   // turn on Digit driver 1
                } 
                else 
                {
                    PORTA = 0b011101;   // turn on Digit driver 1 and SEG_a_DRIVER
                }
            }
            PORTC = DigitSegments;      // turn on segment drivers b to g
            DisplayLED++;               // select next digit
    
            __delay_ms(10);             // Show digit for 10 milliseconds
            
            if(0 == PORTAbits.RA3)      // is SW1 pressed?
            {
                LoopCount++;
                if(LoopCount == 1)
                {
                    DisplayValue++;     // Increment display value every 500 milliseconds
                }
                if(LoopCount >= 50)
                {
                    LoopCount = 0;
                }
            }
            else
            {
                LoopCount = 0;
            }
    
            if(0 == PORTAbits.RA4)      // is SW2 pressed?
            {
                DisplayValue = 0;       // Reset display value to zero
                LoopCount = 0;
            }
        }
    }
    

    This is how I would chnage the above code to produce the random numbers you asked for:

    void main(void) 
    {
        unsigned char DisplayValue, DisplayLED, DigitSegments;
        unsigned char LoopCount;
        unsigned int  Temp;
        
        PORTA = 0;
        PORTC = 0;
        CMCON0 = 7;                 // Turn off Comparators 
        ANSEL = 0;                  // Turn off ADC 
        
        __delay_ms(500);            // wait for ICD before making PGC and PGD outputs;
        TRISA = 0b011100;           // RA5, RA1, RA0 are outputs
        TRISC = 0b000000; 
        OPTION_REGbits.nRAPU = 0;   // Enable weak pull-up on PORTA
        WPUA = 0;                   // Turn off all pull-ups
        WPUAbits.WPUA4 = 1;         // Turn on RA4 pull-up
        
             
        DisplayValue = 0;           // Start Displaying at 0x00 
        DisplayLED = 0;             // Display the 1s first
        LoopCount = 0;
        srand(0x1234);
        
        for(;;)
        {
            PORTC = 0xFF;   // turn off all segment drivers
            PORTA = 0xFF;   // and digit drivers
            if (1 == (DisplayLED & 1))
            {
                DigitSegments = LEDDigit[(DisplayValue >> 4) & 0x0F];
                if(DigitSegments & 0b1000000)
                {
                    PORTA = 0b111110;   // turn on Digit driver 2
                } 
                else 
                {
                    PORTA = 0b011110;   // turn on Digit driver 2 and SEG_a_DRIVER
                }
            }
            else
            {
                DigitSegments = LEDDigit[DisplayValue & 0x0F];
                if(DigitSegments & 0b1000000)
                {
                    PORTA = 0b111101;   // turn on Digit driver 1
                } 
                else 
                {
                    PORTA = 0b011101;   // turn on Digit driver 1 and SEG_a_DRIVER
                }
            }
            PORTC = DigitSegments;      // turn on segment drivers b to g
            DisplayLED++;               // select next digit
    
            __delay_ms(10);             // Show digit for 10 milliseconds
            
            if(0 == PORTAbits.RA3)      // is SW1 pressed?
            {
                LoopCount++;
                if(LoopCount == 1)
                {
                    // Display a new random value every 500 milliseconds
                    Temp = rand() & 0xFFu;      // put random value in range of 0 to 255 and treat is as a fraction in range (0/256) <= value < (255/256)
                    Temp = (Temp * 56u + 0x100u) >> 8; // Use tricky math to make a random number in the range from 1 to 56
                    DisplayValue = (Temp / 10u) << 4;  // Extract the ten's digit
                    DisplayValue = DisplayValue | (Temp % 10); // Extract the one's digit
                }
                if(LoopCount >= 50)
                {
                    LoopCount = 0;
                }
            }
            else
            {
                LoopCount = 0;
            }
    
            if(0 == PORTAbits.RA4)      // is SW2 pressed?
            {
                DisplayValue = 0;       // Reset display value to zero
                LoopCount = 0;
            }
        }
    }