/******* Tones.c *************** * * Use RPG to change tone on RC2/CCP1 output over two octaves, * from C = 523 Hz to C = 2093 Hz. * * VDD = 5V. * Use Fosc = 4 MHz for Fcpu = Fosc/4 = 1 MHz. * Current draw = 2.20 mA * * Current draw without piezo speaker = 2.14 mA * ******* Program hierarchy ***** * * main * Initial * Tone * RPG * ******************************* */ #include // Define PIC18LF4321 registers and bits /******************************* * Configuration selections ******************************* */ #pragma config OSC = INTIO1 // Use internal osc, RA6=Fosc/4, RA7=I/O #pragma config PWRT = ON // Enable power-up delay #pragma config LVP = OFF // Disable low-voltage programming #pragma config WDT = OFF // Disable watchdog timer initially #pragma config WDTPS = 4 // 16 millisecond WDT timeout period, nominal #pragma config MCLRE = ON // Enable master clear pin #pragma config PBADEN = DIG // PORTB<4:0> = digital #pragma config CCP2MX = RB3 // Connect CCP2 internally to RB3 pin #pragma config BOR = SOFT // Brown-out reset controlled by software #pragma config BORV = 3 // Brown-out voltage set for 2.1V, nominal #pragma config LPT1OSC = OFF // Deselect low-power Timer1 oscillator /******************************* * Global variables ******************************* */ unsigned int DELAY; // Counter for obtaining a delay unsigned char ALIVECNT; // Counter for blinking "Alive" LED char COUNT; // Counter available as local to subroutines char DELRPG; // RPG output unsigned int HALFPERIOD; // Value to be reloaded into Timer1 unsigned char OLDPORTB; // Value of PORTB from last loop time const rom int *romPtr; // Pointer into ROM strings signed char NOTENUM; // Number of the note to be played 0,...,12 /******************************* * Constant strings ******************************* */ const rom int Notes[] = {956 ,851,758,716,638,568,506, 478 ,426,379,358,319,284,253, 239}; // Half period microseconds for two octaves /******************************* * Function prototypes ******************************* */ void Initial(void); void RPG(void); void Tone(void); /******************************* * Macros ******************************* */ #define Delay(x) DELAY = x; while(--DELAY){ Nop(); Nop(); } /////// Main program ///////////////////////////////////////////////////// /******************************* * main ******************************* */ void main() { Initial(); // Initialize everything while (1) { RPG(); Tone(); } } /******************************* * Initial * * This function performs all initializations of variables and registers. ******************************* */ void Initial() { OSCCON = 0b01100010; // Use Fosc = 4 MHz (Fcpu = 1 MHz) ADCON1 = 0b00001111; // RA0,1,2,... digital TRISA = 0b00000000; // Set I/O for PORTA TRISB = 0b01110100; // Set I/O for PORTB TRISC = 0b10000000; // Set I/O for PORTC TRISD = 0b00000000; // Set I/O for PORTD TRISE = 0b00000000; // Set I/O for PORTE PORTA = 0; // Set initial state for all outputs PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; Delay(50000); // Pause for half a second RCONbits.SBOREN = 0; // Now disable brown-out reset ALIVECNT = 197; // Blink immediately PORTAbits.RA7 = 1; // Power pullup resistor for RPG Nop(); OLDPORTB = PORTB; // Initialize OLDPORTB PORTAbits.RA7 = 0; // Power down pullup resistor for RPG NOTENUM = 7; // Initial note equals C = 1046 Hz romPtr = Notes; // Point to Notes table HALFPERIOD = *romPtr; // Freq = 880 Hz, initially CCP1CON = 0b00000010; // Toggle CCP1 pin on compare CCPR1 = HALFPERIOD; PIR1bits.CCP1IF = 0; // Clear compare flag TMR1H = 0; // Clear Timer1 TMR1L = 0; T1CON = 0b10000001; // Clock Timer1 from Fcpu = 1 MHz } /******************************* * RPG * * Respond to rising and falling edges on INT2 input from RPG. ******************************* */ void RPG() { unsigned char TEMP; unsigned char newB; DELRPG = 0; PORTAbits.RA7 = 1; // Power pullup resistor for RPG Nop(); TEMP = PORTB; newB = PORTB; PORTAbits.RA7 = 0; // Power down pullup resistor for RPG TEMP = OLDPORTB ^ TEMP; TEMP = TEMP & 0b00110000; if (TEMP != 0) // change? { TEMP = OLDPORTB >> 1; TEMP = newB ^ TEMP; TEMP = TEMP & 0b00010000; if (TEMP != 0) { DELRPG--; //CCW } else { DELRPG++; //CW } } OLDPORTB = newB; } /******************************* * Tone() * * This function waits for TMR1 = CCPR1. * Then it adds HALFPERIOD to CCPR1 and clears CCP1IF flag. * Then it adds DELRPG*16 to HALFPERIOD. */ void Tone() { while (!PIR1bits.CCP1IF); // Wait for flag to set CCPR1 += HALFPERIOD; // Increase CCPR1 by HALFPERIOD PIR1bits.CCP1IF = 0; // Then clear flag NOTENUM += DELRPG; // Update which note to play if (NOTENUM == -1) NOTENUM = 0; if (NOTENUM == 15) NOTENUM = 14; HALFPERIOD = Notes[NOTENUM]; }