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

萝らか妹 提交于 2020-08-08 05:50:18

问题


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

#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();
            
        }
    } 
}


回答1:


@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;
        }
    }
}



回答2:


Your digits are BCD coded(BinaryCodedDecimal), each digit 0-9 is coded in four bits 0000-1001.

DisplayLED toggles between 0 and 1, to select which digit to display, you must ensure you don't update the random value until it have been displayed, actually you should probably update it even more seldom as the display relays on the after glow in the LED and our slow eyes to display an image.

if (DisplayLED == 0) {
  uint8_t r = myrand(); // place your favorite random generator here. 
  DisplayValue = (r/10)<<4|(r%10);
}

Pin 0 and 1 on PortA are flipped by turning them to input and output respectively

TRISA = TRISA ^ 0b000011; // Swap Left/Right (RA0 and RA1)
PORTA = PORTA & 0b111100; // Make Sure Bits are Low 
DisplayLED = DisplayLED ^ 1; // Other Digit Next

Since the least significant bits in TRISA are initiated as 01 they will alternate when negated only one of them is always an output.



来源:https://stackoverflow.com/questions/63006839/pic16f684-dual-seven-seg-display-program-random-number-generator-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!