Pic16f684&双七段显示程序:C语言随机数生成器

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

我正在尝试转换此代码以生成随机数 1-56。 我会理解,但是因为程序必须来回切换才能显示两个数字,所以我很困惑。

我知道在某些方面,我可以使用rand()来选择。

我最终会将此代码与液晶屏结合使用来显示数字以及双七段显示,但现在,只是想办法对 PIC16F684 进行编程以在dua84l 七段显示器

下面是电路和代码。 未包含在图中,但在代码中是 RA3 和 RA4 具有清除显示或生成下一个数字的按钮。

提供的程序在按下按钮时显示 00-0xFF。重写程序可能更容易,但我就是看不懂

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

您的数字是 BCD 编码的(BinaryCodedDecimal),每个数字 0-9 编码为四位 0000-1001。

DisplayLED 在 0 和 1 之间切换,到 select 要显示的数字,你必须确保在显示之前不要更新随机值,实际上你应该更新它甚至更少,因为显示器依靠 LED 的余辉和我们缓慢的眼睛来显示图像。

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

通过将 PortA 上的引脚 0 和 1 分别变为输入和输出来翻转它们

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

由于 TRISA 中的最低有效位被初始化为 01 它们将在取反时交替出现,只有其中一个始终是输出。

@tjpplay,

您发布的代码有一些细微的问题,并且无法交替启用数字驱动程序。

您检测按钮按下的方法会扰乱数字多路复用器的时序并导致闪烁。

将数字驱动器连接到用于在线串行编程器 (ICSP) 的 PGC 和 PGD 引脚会使电路编程变得困难。在线调试 (ICD) 不适用于此实现。

该代码仅对 PORTA 和 PORTC 寄存器使用 8 位写入,从而避免了新玩家的读取-修改-写入 (RMW) 陷阱。

使用允许 C 编译器执行单个位设置和清除的语法可能会对 PIC16F684 等控制器产生 RMW 问题,尤其是在直接驱动 LED 时。

我认为这段代码可能适用于您的硬件:

/*
 * 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;
        }
    }
}

这就是我如何修改上面的代码来生成您要求的随机数:

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